Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

VOA

This project provides Rust libraries, command line tools and a test suite to interact with the File Hierarchy for the Verification of OS Artifacts (VOA).

The VOA specification has been created to provide the means for a generic, OS artifact verification scheme, that can work with different technologies while relying on a unifying lookup directory to retrieve verifiers for signatures.

This project provides a reference implementation for the VOA specification as well as a canonical test suite that is usable by any other implementation.

Currently, only an OpenPGP backend is specified. However, specifications for the minisign, signify, SSH and X.509 backends are already prepared and only require some further input for finalizing. If you have expertise in one of these technologies, please get in touch on the relevant pull request for specification.

This project is currently supported by the Sovereign Tech Agency. Read the official announcement for more information.

Documentation

The latest project documentation can be found at https://voa.archlinux.page

Overview

The following mindmap attempts to provide a high-level overview of the project and put (existing and upcoming) libraries and command line tools into context.

mindmap
  root((📄 VOA specification))
    ⌨️/📚️ voa
    📚️ voa-config
    📚️ voa-core
    📚️ voa-minisign *
    📚️ voa-openpgp
    📚️ voa-signify *
    📚️ voa-ssh *
    📚️ voa-x509 *
    ⌨️ voa-verify *
    📚️ voa-test-suite *

[*] Not yet implemented, or subject to change.

Components

Currently the following components are available:

  • voa: command line interface and library for interacting with VOA
  • voa-config: a library for configuring VOA technology backends
  • voa-core: a library for access to verifiers in VOA hierarchies
  • voa-openpgp: a library for using OpenPGP verifiers in VOA

Contributing

Please refer to the contribution guidelines to learn how to contribute to this project.

Releases

Releases of components are created by the developers of this project.

OpenPGP certificates with the following OpenPGP fingerprints can be used to verify signed tags:

The above are part of archlinux-keyring and certified by at least three main signing keys of the distribution.

License

This project can be used under the terms of the Apache-2.0 or MIT. Contributions to this project, unless noted otherwise, are automatically licensed under the terms of both of those licenses.

VOA

A command line interface and library for interacting with the “File Hierarchy for the Verification of OS Artifacts” (VOA).

Documentation

Installation

It is recommended to install the voa CLI using your distribution’s package management system. If it is not yet available there, ask your friendly distribution package maintainer to package it for you!

Building from source

In the meantime, it is of course always possible to install the latest released version of the voa CLI from https://crates.io for your user with a recent version of Rust:

cargo install --locked --features cli voa

When using the source repository of the project, the path has to be specified:

cargo install --locked --features cli --path voa

Man pages

Man pages for the CLI can be created by calling the following in the root of the source repository:

just generate manpages voa

The man pages are placed into output/manpages/ by default.

For completeness sake, also consider the installation instructions of the voa-config crate.

Shell completions

Completions for various shells are available and can be created by calling the following in the root of the source repository:

just generate shell_completions voa

The shell completion files are placed into output/shell_completions/ by default.

Examples

Library

use std::io::Write;

use tempfile::{NamedTempFile, tempdir};
use voa::commands::{load_verifier, search_verifiers, write_verifier_to_hierarchy};

fn main() -> testresult::TestResult {
// Write a generic OpenPGP certificate to a temporary file.
let cert = r#"-----BEGIN PGP PUBLIC KEY BLOCK-----

xjMEaNBDAhYJKwYBBAHaRw8BAQdAzjzrpQ/AEteCmzjd1xTdXGaHV0VKSm4HLy6l
HVcmWT3NH0pvaG4gRG9lIDxqb2huLmRvZUBleGFtcGxlLm9yZz7CmgQQFggAQgUC
aNBDAhYhBEauMg3lOimFWKbyoPtSEBy0DfYKAhsDAh4BBAsJCAcGFQ4KCQwIARYN
JwkCCAIHAgkBCAEHAQIZAQAKCRD7UhActA32CkhIAP9bhoLJeZRCAc+q1kFEkstT
uXBPlzHagF6ghuUfToMmVQD+KaakONKSekglKR4rJxzhleQJ4qsptt1gjXX13QgF
Xwo=
=Pkv9
-----END PGP PUBLIC KEY BLOCK-----"#;
let mut temp_file = NamedTempFile::new()?;
write!(temp_file, "{cert}")?;
let input_path = temp_file.path();

// Load OpenPGP verifier from file.
let verifier = load_verifier(Some(input_path.try_into()?), "openpgp".parse()?)?;

// Prepare a temporary output directory.
let temp_dir = tempdir()?;

// Write a verifier to a location in a temporary VOA hierarchy.
write_verifier_to_hierarchy(verifier, temp_dir, "os".parse()?, "packages".parse()?, None)?;

// Search for verifiers by relevant identifier information.
let verifiers = search_verifiers("os".parse()?, "packages".parse()?, None, None)?;

for verifier in verifiers.keys() {
  println!("{verifier:?}");
}
Ok(())
}

Verification

This crate offers an API for artifact verification based on a voa-config trust model configuration. With the OpenPGP verification technology, artifact verification can be performed as follows:

use std::{collections::HashSet, num::NonZero, str::FromStr};
use voa::{
    commands::{
        PurposeAndContext,
        get_voa_config,
        get_technology_settings,
        openpgp_verify,
        read_openpgp_signatures,
        read_openpgp_verifiers,
    },
    openpgp::ModelBasedVerifier,
    utils::RegularFile,
};
use voa_config::{openpgp::{NumDataSignatures, OpenpgpSettings, PlainMode, VerificationMethod}};

fn main() -> testresult::TestResult {
let signatures = read_openpgp_signatures(
    &HashSet::from_iter(vec![RegularFile::from_str("/some/path/to/an/file.tar.zst.sig")?])
)?;
    
let verifiers = read_openpgp_verifiers("os".parse()?, "purpose".parse()?, "context".parse()?);
let anchors = read_openpgp_verifiers("os".parse()?, "trust-anchor-purpose".parse()?, "context".parse()?);

let config = get_voa_config();

let openpgp_settings =
    get_technology_settings(&config, &"os".parse()?, PurposeAndContext::new(None, None).as_ref())
        .openpgp_settings();
let model = ModelBasedVerifier::new(openpgp_settings, &verifiers, &anchors);

openpgp_verify(
    &model,
    &signatures,
    &RegularFile::from_str("/some/path/to/an/file.tar.zst")?,
)?;
Ok(())
}

CLI

The voa CLI offers a simple interface for dealing with data in a VOA hierarchy.

Import verifiers

Verifiers can be imported using the voa import subcommand.

Assuming that the environment variable OPENPGP_CERT contains the path to an OpenPGP certificate with signing capabilities, we can import it to a VOA hierarchy directory (represented by the VOA_DIR environment variable).

The following imports the OpenPGP certificate to the directory os/packages/openpgp/ in VOA_DIR, implying that this verifier is to be used for the verification of package files on the OS os.

rpgp show "$OPENPGP_CERT"
voa import os packages openpgp --input "$OPENPGP_CERT" --base-path "$VOA_DIR"
# 🔐 EdDSA/Curve25519 v4 f992bda338ded64fe062302b5bd40d64577b8ea2
#  ⏱ Created 2025-09-20 06:13:33 UTC
#
#   🪪 ID "John Doe <john.doe@example.org>"
#     🖋 CertGeneric 2025-09-20 06:13:33 UTC, by 5bd40d64577b8ea2 [EdDSALegacy, SHA256, V4]
#
tree "$VOA_DIR"
# .
# └── os
#     └── packages
#         └── default
#             └── openpgp
#                 └── f992bda338ded64fe062302b5bd40d64577b8ea2.openpgp
rpgp show "$VOA_DIR"/os/packages/default/openpgp/*.openpgp
# 🔐 EdDSA/Curve25519 v4 f992bda338ded64fe062302b5bd40d64577b8ea2
#  ⏱ Created 2025-09-20 06:13:33 UTC
#
#   🪪 ID "John Doe <john.doe@example.org>"
#     🖋 CertGeneric 2025-09-20 06:13:33 UTC, by 5bd40d64577b8ea2 [EdDSALegacy, SHA256, V4]
#

List verifiers

Verifiers can be listed using the voa list subcommand.

voa list os packages
# /home/user/.config/voa/os/packages/default/openpgp/f992bda338ded64fe062302b5bd40d64577b8ea2.openpgp
voa list os packages --output-format json
# [{"load_path":{"load_path":"/home/user/.config/voa","writable":true,"ephemeral":false},"verifier_path":"/home/user/.config/voa/arch/packages/default/openpgp/f992bda338ded64fe062302b5bd40d64577b8ea2.openpgp","os":{"id":"arch","version_id":null,"variant_id":null,"image_id":null,"image_version":null},"purpose":{"role":"packages","mode":""},"context":"default","technology":"openpgp"}]

List origins of available technology settings

Technology settings for an OS or for specific contexts of an OS are loaded from voa configuration files. Which origins are used for all of them can be listed using the voa config list subcommand.

voa config list
# 🖥 example
# ⤷ Config file: /usr/share/voa/example.yaml
# ⤷ Built-in defaults
# ➕ example/image/installation-medium
# ⤷ Config file: /usr/share/voa/example.yaml
# ⤷ Config file: /usr/share/voa/example.yaml
# ⤷ Built-in defaults
# ➕ example/package/default
# ⤷ Config file: /usr/share/voa/example.yaml
# ⤷ Config file: /usr/share/voa/example.yaml
# ⤷ Built-in defaults
# ➕ example/repository-metadata/test-repo
# ⤷ Config file: /usr/share/voa/example.yaml
# ⤷ Built-in defaults
voa config list --output-format json | jq
# {
#   "os": {
#     "example": [
#       {
#         "config_file": "/usr/share/voa/example.yaml"
#       },
#       "default"
#     ]
#   },
#   "os_purpose_context": {
#     "example/image/installation-medium": [
#       {
#         "config_file": "/usr/share/voa/example.yaml"
#       },
#       {
#         "config_file": "/usr/share/voa/example.yaml"
#       },
#       "default"
#     ],
#     "example/package/default": [
#       {
#         "config_file": "/usr/share/voa/example.yaml"
#       },
#       {
#         "config_file": "/usr/share/voa/example.yaml"
#       },
#       "default"
#     ],
#     "example/repository-metadata/test-repo": [
#       {
#         "config_file": "/usr/share/voa/example.yaml"
#       },
#       "default"
#     ]
#   }
# }

Show technology settings

Based on the voa config file format, technology settings can be set generically for an OS, or for specific contexts of an OS. These technology settings can be shown using the voa config show subcommand.

voa config show example
# OpenPGP settings
#
# 🔏 Each artifact requires 1 valid data signature(s) from artifact verifiers to be successfully verified.
#
# ✅ Each artifact is verified using the "trust anchor" verification method.
#
# 📧 A valid certificate is not required to use a specific domain in any of its User IDs, but needs 3 certification(s) from individual trust anchors on one User ID for the certificate to be considered as artifact verifier.
#
# 🐾 A valid certificate is not required to match a specific OpenPGP fingerprint to be considered as trust anchor.
#
# 📝 The following sources have been considered for the creation of the settings:
# ⤷ Built-in defaults
voa config show --output-format json --purpose purpose --context context example | jq
# {
#   "origins": [
#     "default"
#   ],
#   "openpgp": {
#     "num_data_signatures": 1,
#     "verification_method": {
#       "trust_anchor": {
#         "required_certifications": 3,
#         "artifact_verifier_identity_domain_matches": [],
#         "trust_anchor_fingerprint_matches": []
#       }
#     }
#   }
# }

Verify artifacts

⚠️ NOTE: Artifact verification using the OpenPGP technology backend currently does not yet support the “Web of Trust” verification method exposed by voa-config.

OS artifacts can be verified using the voa verify subcommand. Currently, only OpenPGP verification is available.

voa verify os image default /path/to/an/image-1.2.3.tar.zst /path/to/an/image-1.2.3.tar.zst.sig
# ✅ /path/to/an/image-1.2.3.tar.zst.sig 1734644649 f1d2d2f924e986ac86fdf7b36c94bcdf32beec15 e242ed3bffccdf271b7fbaf34ed72d089537b42f
voa verify os image default /path/to/an/image-1.2.3.tar.zst /path/to/an/image-1.2.3.tar.zst.sig --output-format json
# [{"signature":"/path/to/an/image-1.2.3.tar.zst.sig","result":{"valid":{"signature_creation_time":1734644649,"primary_key_fingerprint":"f1d2d2f924e986ac86fdf7b36c94bcdf32beec15","verifying_component_key_fingerprint":"e242ed3bffccdf271b7fbaf34ed72d089537b42f"}}}]

Contributing

Please refer to the contribution guidelines to learn how to contribute to this project.

License

This project can be used under the terms of the Apache-2.0 or MIT. Contributions to this project, unless noted otherwise, are automatically licensed under the terms of both of those licenses.

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[Unreleased]

[0.7.0] - 2025-12-23

Added

  • [breaking] Implement trust model-based verification
  • Add ArtifactVerifierPurpose to ensure non-trust-anchor Purpose

[0.6.0] - 2025-12-19

Added

  • (cli) Add the voa config subcommand
  • Add get_technology_settings for OS/context TechnologySettings
  • Add get_voa_config function to return the system’s VoaConfig

Fixed

  • Use panic instead of returning TestError

Other

  • Add examples for showing technology settings

[0.5.0] - 2025-11-20

Other

  • [breaking] Use VerifierLookup for signature verification

[0.4.2] - 2025-11-18

Other

  • update Cargo.lock dependencies

[0.4.1] - 2025-11-13

Added

  • Improve logging when reading certificates and verifiers
  • Add public re-exports for items of voa-core and voa-openpgp

[0.4.0] - 2025-11-12

Added

  • (cargo) Remove cli from the default features

Fixed

  • Hide Error::JsonSerialization behind the cli feature

Other

  • Explain how to build the CLI using the cli feature

[0.3.0] - 2025-11-12

Added

  • [breaking] Return a Vec of structs from voa_openpgp::verify_from_file
  • [breaking] Adjust signature of voa_openpgp::verify_from_file

[0.2.1] - 2025-10-30

Other

  • update Cargo.lock dependencies

[0.2.0] - 2025-09-29

Added

  • Add the voa verify subcommand and high-level verify function
  • Add RegularFile type for paths that must be a regular file
  • Add the voa list subcommand to list verifiers in VOA
  • Move clap and clap-verbosity-flag to workspace dependencies

Other

  • Add documentation examples on voa verify and verify function
  • [breaking] Rename Technology::OpenPGP to Technology::Openpgp
  • (README) Add examples for voa list and search_verifiers

[0.1.0] - 2025-09-24

Added

  • Add the voa import subcommand
  • Initialize bare voa crate

Other

  • (README) Add examples for library and the voa import subcommand

VOA-config

A library for the configuration of technology backends in the “File Hierarchy for the Verification of OS Artifacts” (VOA).

Documentation

This library defines the voa configuration file format.

Installation

This crate does not expose a command line interface, but is instead used by the voa crate.

Man pages

The man page for the configuration file format can be created by calling the following in the root of the source repository (requires lowdown to be installed):

just generate specifications voa-config

The man page is placed into output/manpages/ by default.

Examples

use voa_config::VoaConfig;

fn main() -> testresult::TestResult {
// Load all available and valid configuration files.
let config = VoaConfig::load();

// Query the settings for an OS "example"
let _settings = config.settings_for_os_or_default(&"example".parse()?);

// Query the settings for an OS "example" with purpose "package" and context "my-repo".
let _settings = config.settings_for_context_or_default(&"example".parse()?, &"package".parse()?, &"my-repo".parse()?);
Ok(())
}

Contributing

Please refer to the [contribution guidelines] to learn how to contribute to this project.

License

This project can be used under the terms of the Apache-2.0 or MIT. Contributions to this project, unless noted otherwise, are automatically licensed under the terms of both of those licenses.

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[Unreleased]

[0.1.1] - 2025-12-19

Added

  • Add VoaConfig::contexts to return OsPurposeContext settings
  • Add VoaConfig::oses to return the map of Os settings

Fixed

  • Properly assign config origins based on location

Other

  • Improve the use of ConfigOrigin
  • Fix links to Rust documentation

[0.1.0] - 2025-12-17

Added

  • Add configuration file handling
  • Initialize bare voa-config crate

Other

  • (README) Add code examples and man pages to voa-config crate
  • Add section 5 man page for voa

File Hierarchy for the Verification of OS Artifacts (VOA)

VOA is mechanism for the storage and retrieval of cryptography technology-agnostic signature verifiers.

For specification see: https://uapi-group.org/specifications/specs/file_hierarchy_for_the_verification_of_os_artifacts/.

Documentation

Purpose

The VOA hierarchy acts as structured storage for files that contain “signature verifiers” (such as OpenPGP certificates, aka “public keys”).

Load Paths

A set of “load paths” may exist on a system, each containing different sets of verifier files. This library provides an abstract, unified view of sets of signature verifiers in these load paths.

Earlier load paths have precedence over later entries (in some technologies).

VOA operates either in “system mode” or “user mode”, depending on the UID of the running process. The set of load paths differs between the two modes.

In system mode, the set of load paths (in order of descending precedence) is:

  • /etc/voa/
  • /run/voa/
  • /usr/local/share/voa/
  • /usr/share/voa/

In user mode, the analogous set of load paths (in order of descending precedence) is:

  • $XDG_CONFIG_HOME/voa/
  • the ./voa/ directory in each directory defined in $XDG_CONFIG_DIRS
  • $XDG_RUNTIME_DIR/voa/
  • $XDG_DATA_HOME/voa/
  • the ./voa/ directory in each directory defined in $XDG_DATA_DIRS

Support for different cryptographic technologies

Libraries dealing with a specific cryptographic technology can rely on this library to collect the paths of all verifier files relevant to them.

NOTE
Depending on the technology, multiple versions of the same verifier will either “shadow” one another, or get merged into one coherent view that represents the totality of available information about the verifier.

Shadowing/merging is specific to each signing technology and must be handled in the technology-specific library. For more details see e.g. the voa-openpgp implementation and the VOA specification.

VOA expects that filenames are a strong identifier that signals whether two verifier files contain variants of “the same” logical verifier. Verifiers from different load paths can be identified as related via their filenames.

For example, OpenPGP certificates must be stored using filenames based on their fingerprint. When the filename doesn’t match the fingerprint of an OpenPGP verifier, the file is considered invalid and ignored.

Example VOA directory structure

In this example, three OpenPGP certificates are stored as verifiers. They are stored in two system-mode load paths: /etc/voa and /usr/local/share/voa/:

/etc/voa/arch/packages/default/openpgp/0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33.pgp
/etc/voa/arch/packages/default/openpgp/62cdb7020ff920e5aa642c3d4066950dd1f01f4d.pgp
/usr/local/share/voa/arch/packages/default/openpgp/bbe960a25ea311d21d40669e93df2003ba9b90a2.pgp

The four VOA subdirectory layers are the same for each of the verifiers: arch/packages/default/openpgp.

These layers signify, respectively:

  • the os identifier is arch,
  • the verifier purpose is package,
  • the verifier context is default and
  • the verifier technology is openpgp.

In a fully populated VOA structure, verifier files may be stored for different use cases, and stored in a mix of different os, purpose, context and technology paths.

Integration tests

The integration tests in this crate set up VOA file hierarchies in a container environment and consume/evaluate them with voa-core. They can be run with just containerized-integration-tests.

Usage

This crate is not usually used directly in applications. Its main purpose is as a foundational building block for technology-specific VOA libraries, such as voa-openpgp.

Technology-specific VOA implementations are, in turn, used in applications (e.g. an application that validates signatures over distribution packages before installation).

use voa_core::{
    Voa,
    identifiers::{Context, Mode, Os, Purpose, Role, Technology},
};

fn main() -> Result<(), voa_core::Error> {
// Create a new VOA instance.
// Its load paths are automatically set up based on the user id of the running process.
let voa = Voa::new();

// Look up OpenPGP verifiers for the Os `arch` to validate signatures on `packages`.
let verifiers = voa.lookup(
    Os::new("arch".parse()?, None, None, None, None),
    Purpose::new(Role::Packages, Mode::ArtifactVerifier),
    Context::Default,
    Technology::Openpgp,
);
Ok(())
}

Features

  • _containerized-integration-test for integration tests that run in a dedicated container.
  • _winnow-debug enables the winnow/debug feature, which shows the exact parsing process of winnow.

Contributing

Please refer to the contribution guidelines to learn how to contribute to this project.

License

This project can be used under the terms of the Apache-2.0 or MIT. Contributions to this project, unless noted otherwise, are automatically licensed under the terms of both of those licenses.

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[Unreleased]

[0.3.2] - 2025-12-23

Added

  • Add Purpose::to_trust_anchor to convert a Purpose to trust anchor
  • Add Purpose::is_trust_anchor checking if Purpose is trust anchor

[0.3.1] - 2025-12-17

Added

  • Derive PartialOrd and Ord for Purpose
  • Derive PartialOrd and Ord and Role
  • Derive PartialOrd and Ord for CustomRole
  • Derive PartialOrd and Ord for Mode
  • Derive PartialOrd and Ord for Context
  • Derive PartialOrd and Ord for CustomContext
  • Derive PartialOrd and Ord for Os
  • Derive PartialOrd and Ord for IdentifierString
  • Implement TryFrom<&OsStr> for Os
  • Derive Eq and Hash for Purpose
  • Derive Eq and Hash for Role
  • Derive Eq and Hash for CustomRole
  • Derive Eq and Hash for Mode
  • Derive Hash for Context
  • Derive Hash for CustomContext
  • Derive Eq and Hash for Os
  • Derive Hash for Technology
  • Derive Hash for CustomTechnology
  • Derive Hash for IdentifierString
  • Make Os::os_to_string public
  • Make Purpose::purpose_to_string public
  • Rename Role::Custom to custom when (de)serializing
  • Rename Context::Custom to custom when (de)serializing
  • Derive serde::Deserialize for Context
  • Derive serde::Deserialize for CustomContext
  • Derive serde::Deserialize for Os
  • Derive serde::Deserialize for Purpose
  • Derive serde::Deserialize for Mode
  • Derive serde::Deserialize for Role
  • Derive serde::Deserialize for CustomRole
  • Derive serde::Deserialize for IdentifierString

Fixed

  • Use panic instead of returning TestError

Other

  • (cargo) Move crate change-user-run to workspace dependencies
  • (cargo) Move crate xdg to workspace dependencies
  • (cargo) Move crate directories to workspace dependencies

[0.3.0] - 2025-09-29

Added

  • (voa-core) Derive Clone for Verifier
  • Add serde feature for serde support on all identifiers
  • Move clap and clap-verbosity-flag to workspace dependencies
  • Return LoadPath instead of Path from VoaLocation::load_path
  • Add LoadPath::path returning the underlying path
  • Add LoadPath::ephemeral to check whether the path is ephemeral

Other

  • [breaking] Rename Technology::OpenPGP to Technology::Openpgp
  • (cargo) Move serde and serde_json to workspace dependencies
  • (cargo) Move strum to workspace dependencies

[0.2.1] - 2025-09-24

Added

  • Add LoadPathFilter to allow filtering a LoadPathList
  • Add LoadPathList::from_effective_user for current user load paths
  • Publicly expose LoadPath and LoadPathList

Other

  • (cargo) Move anyhow from dependencies to dev-dependencies
  • (cargo) Move libc to workspace dependencies
  • (cargo) Add common dependencies to workspace dependencies

[0.2.0] - 2025-09-10

Added

  • Add VerifierWriter trait to support writing verifiers
  • [breaking] Replace the use of TryFrom<&str> for Role with Role::from_str
  • Add winnow parsers and FromStr impls for VOA path components
  • Add IdentifierString as base for components with custom strings

Other

  • [breaking] Use IdentifierString instead of check_part utility
  • Add link for documentation of released crate versions

[0.1.0] - 2025-08-30

Added

  • Basic implementation of voa core spec

Other

  • Add containerized integration tests

VOA-OpenPGP

A library for using OpenPGP verifiers in VOA.

See https://uapi-group.org/specifications/specs/file_hierarchy_for_the_verification_of_os_artifacts/#openpgp

Documentation

Examples

Import

OpenPGP certificates can be written to their dedicated directory structures in a VOA hierarchy.

It is supported to import single binary or ASCII-armored files, as well as directory structures that contain a number of OpenPGP packet files which comprise an OpenPGP certificate when concatenated (this structured form is in use by the archlinux-keyring project).

use voa_core::VerifierWriter;
use voa_openpgp::OpenPgpImport;
use pgp::{
    composed::{KeyType, SecretKeyParamsBuilder, SignedPublicKey, SubkeyParamsBuilder},
    ser::Serialize,
    types::Password,
};
use rand::thread_rng;
use tempfile::{NamedTempFile, tempdir};

fn openpgp_cert() -> testresult::TestResult<SignedPublicKey> {
    let mut signkey = SubkeyParamsBuilder::default();
    signkey
        .key_type(KeyType::Ed25519Legacy)
        .can_sign(true)
        .can_encrypt(false)
        .can_authenticate(false);
    let mut key_params = SecretKeyParamsBuilder::default();
    key_params
        .key_type(KeyType::Ed25519Legacy)
        .can_certify(true)
        .can_sign(false)
        .can_encrypt(false)
        .primary_user_id("John Doe <jdoe@example.org>".to_string())
        .subkeys(vec![signkey.build()?]);

    let secret_key_params = key_params.build()?;
    let secret_key = secret_key_params.generate(thread_rng())?;

    // Produce binding self-signatures that link all the components together
    let signed = secret_key.sign(&mut thread_rng(), &Password::from(""))?;

    let pubkey = SignedPublicKey::from(signed);
    Ok(pubkey)
}
fn main() -> testresult::TestResult {

// Write a generic OpenPGP certificate to a temporary file.
let mut temp_file = NamedTempFile::new()?;
openpgp_cert()?.to_writer(&mut temp_file)?;
let input_path = temp_file.path();

// Import the OpenPGP certificate.
let import = OpenPgpImport::from_file(input_path)?;

// Prepare a temporary output directory.
let temp_dir = tempdir()?;
let output_dir = temp_dir.path();

// Write the OpenPGP verifier to a VOA hierarchy in the temporary output directory.
//
// There, the verifier is written to the configured directory, e.g.
// `os/purpose/context/openpgp/f1d2d2f924e986ac86fdf7b36c94bcdf32beec15.openpgp`
import.write_to_hierarchy(
    output_dir,
    "os".parse()?,
    "purpose".parse()?,
    Some("context".parse()?),
)?;

assert!(
    output_dir
        .join("os")
        .join("purpose")
        .join("context")
        .join("openpgp")
        .join(import.file_name())
        .exists()
);
Ok(())
}

Verification

Simple verification of artifacts using one or more OpenPGP signatures and verifiers from VOA is straight forward:

use std::{num::NonZero, path::PathBuf};
use voa_config::openpgp::{NumDataSignatures, OpenpgpSettings, PlainMode, VerificationMethod};
use voa_openpgp::{ModelBasedVerifier, OpenpgpCert, OpenpgpSignature, VoaOpenpgp};

fn main() -> testresult::TestResult {
let voa = VoaOpenpgp::new();
let certs = voa.lookup("os".parse()?, "purpose".parse()?, "context".parse()?);

let config = OpenpgpSettings::new(
    NumDataSignatures::new(NonZero::new(1).expect("1")),
    VerificationMethod::Plain(PlainMode::default())).expect("OpenpgpSettings::new");
let model = ModelBasedVerifier::new(&config, &certs, &[]);

let file = PathBuf::from("/path/to/a/file.tar.zst");
let sigs = &[OpenpgpSignature::from_file(&PathBuf::from("/path/to/a/file.tar.zst.sig"))?];
let result = model.verify_file_with_signatures(&file, &sigs[..])?;

Ok(())
}

Contributing

Please refer to the contribution guidelines to learn how to contribute to this project.

License

This project can be used under the terms of the Apache-2.0 or MIT. Contributions to this project, unless noted otherwise, are automatically licensed under the terms of both of those licenses.

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[Unreleased]

[0.6.0] - 2025-12-23

Added

  • [breaking] Implement trust model-based verification
  • Implement FromIterator for VerifierLookup
  • Add VerifierLookup::valid_userid_certifications

Fixed

  • Use panic instead of returning TestError

Other

  • Improve VerifierLookup comments
  • Ensure stable ordering in VerifierLookup::candidate_certs
  • Return OpenpgpFingerprint for OpenpgpCert::fingerprint

[0.5.0] - 2025-11-20

Added

  • Add VerifierLookup to facilitate signature verification

Other

  • Add test to use VoaOpenpgp::verify_from_file with a signing subkey
  • [breaking] Use VerifierLookup for signature verification

[0.4.2] - 2025-11-18

Other

  • (deps) Update Rust crates pgp to 0.18.0 and rpgpie to 0.8.0

[0.4.1] - 2025-11-13

Added

  • Improve logging during signature verification of files

[0.4.0] - 2025-11-12

Added

  • [breaking] Return a Vec of structs from voa_openpgp::verify_from_file
  • [breaking] Make voa_openpgp::verify_from_file more flexible for paths
  • [breaking] Adjust signature of voa_openpgp::verify_from_file

[0.3.0] - 2025-10-30

Added

  • Make import from archlinux-keyring structures more robust

[0.2.0] - 2025-09-29

Added

  • (voa-openpgp) Implement verifier lookup, package validation

Other

  • (README) Add example for verify_from_file
  • (voa-openpgp) Container tests for verifiers
  • [breaking] Rename Technology::OpenPGP to Technology::Openpgp

[0.1.1] - 2025-09-24

Added

  • (cargo) Add voa-core and voa-openpgp to workspace dependencies

Other

  • (cargo) Move pgp and rand to workspace dependencies
  • (cargo) Add common dependencies to workspace dependencies

[0.1.0] - 2025-09-10

Added

  • Add import module for import of OpenPGP verifiers
  • Initialize bare voa-openpgp crate

Other

  • Upgrade voa-core crate to 0.2.0

NAME

voa - Configuration file format for VOA technology backends.

DESCRIPTION

A YAML configuration file that encodes the OS-level and/or context-level settings for technology backends used in the File Hierarchy for the Verification of OS Artifacts (VOA[1]).

Each configuration file tracks a VOA OS identifier in its file name, (e.g. “arch.yaml” for the “arch” OS, “debian:13.yaml” for the “debian:13” OS - refer to the VOA specification for details on the allowed character set).

Each configuration file must be a regular file to be considered.

Directories and precedence

The handling of configuration files is based on the Configuration Files Specification[2].

For system users voa files are read from the system VOA directories “/usr/share/voa/” and “/usr/local/share/voa/”, the volatile runtime directory “/run/voa/” and the local administration directory “/etc/voa/”. For real users, in addition to the system-wide configuration directories, voa files are also read from “voa/” directories in “$XDG_CONFIG_DIRS” and from the “voa/” directory in “$XDG_CONFIG_HOME” (see XDG Base Directory Specification[3]).

The order of the aforementioned directories describes the order of precedence in increasing priority. A file from a directory with higher priority fully replaces one from a directory with lower priority (e.g. “/etc/voa/example.yaml” replaces “/usr/share/voa/example.yaml”, “~/.config/voa/example.yaml” replaces “/etc/voa/example.yaml”).

The voa configuration file format can also be used in drop-in configuration files. A drop-in is created for a specific OS by adding a voa configuration file in an override directory in one of the configuration directories (e.g. “/etc/voa/example.yaml.d/99-custom-repo.yaml” providing settings for a custom repository on the “example” OS, which overrides the more generic technology settings of the OS in “/usr/share/voa/example.yaml”). Note that drop-in configurations are read in lexicographic order, based on their file name, regardless of the directory precedence of their location.

Each voa configuration file can be masked by creating a symlink to /dev/null in its place (e.g. “/etc/voa/example.yaml -> /dev/null” will cause the file “/usr/share/voa/example.yaml” to not be considered).

Merging of built-in defaults, OS and context-level settings

When deriving OS or context-level technology settings from voa configuration files, built-in defaults may be considered.

After reading all configuration and drop-in configuration files, the available OS-level technology settings are constructed from them, with unset fields populated using already existing OS-level settings, or built-in defaults.

Eventually, the available context-level technology settings are constructed from the configuration and drop-in configuration files, with unset fields populated using - depending on availability - OS-level defaults, or built-in defaults.

NOTE: The following merging logic applies - unless stated otherwise - for all optional fields in the configuration file objects: If an optional field is omitted or set to null, a concrete value for it is derived either from OS-level, or if not provided there either, built-in defaults. While this allows for rich override and inheritance logic, it also implies the necessity to be explicit where necessary!

If an OS is not covered by a specific configuration or drop-in configuration file, the built-in defaults apply.

FILE FORMAT

The file must contain valid YAML data and all of the following described required fields and values must be present. Invalid voa files are ignored.

“default_technology_settings” object

The top-level object in the file which contains option objects for each available technology backend used in VOA.

WARNING: For the voa file as a whole to be considered valid, this object must be defined, if the “contexts” list is empty!

“openpgp” object

The object describes the settings for the OpenPGP backend. Verification is based on OpenPGP signatures[4] and OpenPGP certificates[5]. Several verification methods are available.

“num_data_signatures” number

The optional number of OpenPGP signatures from individual issuing OpenPGP certificates required for an artifact to be considered verified.

If set, the value must be a non-zero, positive integer (built-in default: 1).

“verification_method” object

The method used to verify OpenPGP signatures using OpenPGP certificates. The value must be one of the following objects.

“plain” verification method object

The “plain” verification method for OpenPGP signatures is a basic form of verification, that only considers artifact verifiers but no trust anchors. With it, one or more of the found artifact verifiers for a verification query must be used to successfully verify one or more OpenPGP signatures.

To constrain the set of OpenPGP certificates allowed as artifact verifiers, it can be filtered by domain name matches against valid OpenPGP User IDs[6] and/or OpenPGP fingerprint[7]. If no filters are defined, all OpenPGP certificates found in an artifact verifier location of a given context are considered as artifact verifiers.

“identity_domain_matches” set

An optional set of valid domain names, that is used to filter artifact verifiers.

If the set has entries, a valid OpenPGP certificate found in the artifact verifier location of a given context is only considered if one of its valid OpenPGP User IDs matches one of the domain names.

If the set is empty (the built-in default), no filtering based on domain names occurs and any valid OpenPGP certificate found in the artifact verifier location of a given context is considered (assuming no other filter prevents it).

“fingerprint_matches” set

An optional set of OpenPGP fingerprints, that is used to filter artifact verifiers.

WARNING: The number of entries in this set must be equal to or greater than the number of required data signatures (see “num_data_signatures”) for the verification method as a whole to be considered valid!

If the set has entries, a valid OpenPGP certificate found in the artifact verifier location of a given context is only considered if its OpenPGP fingerprint matches one of the fingerprints from the set.

If the set is empty (the built-in default), no filtering based on fingerprints occurs and any valid OpenPGP certificate found in the artifact verifier location of a given context is considered (assuming no other filter prevents it).

“trust_anchor” verification method object

The “trust anchor” verification method for OpenPGP signatures is a basic form of trust anchor based verification, that considers both artifact verifiers and trust anchors when verifying a signature. With it, each artifact verifier must be certified by a specific number of the available trust anchors to be considered for the verification of OpenPGP signatures.

To constrain the set of valid OpenPGP certificates allowed as artifact verifier, it can be filtered by domain name matches against valid OpenPGP User IDs. To constrain the set of valid OpenPGP certificates allowed as trust anchor, it can be filtered by OpenPGP fingerprints.

“required_certifications” number

The optional number of OpenPGP third-party certifications[8] of trust anchors on artifact verifiers for an artifact verifier to be considered fully trusted. If set, the value must be a non-zero, positive integer (built-in default: 3).

“artifact_verifier_identity_domain_matches” set

An optional set of valid domain names, that is used to filter artifact verifiers.

If the set has entries, a valid OpenPGP certificate found in the artifact verifier location of a given context is only considered if one of its valid OpenPGP User IDs matches one of the domain names from the set and it has the required amount of OpenPGP third-party certifications from valid trust anchors.

If the set is empty (the built-in default), no filtering based on domain names occurs and any valid OpenPGP certificate found in the artifact verifier location which has at least one OpenPGP User ID with the required amount of OpenPGP third-party certifications from valid trust anchors is considered.

“trust_anchor_fingerprint_matches” set

An optional set of OpenPGP fingerprints, that is used to filter trust anchors.

WARNING: The number of entries in this set must be equal to or greater than the number of required certifications (see “required_certifications”) for the verification method as a whole to be considered valid!

If the set has entries, a valid OpenPGP certificate found in the trust anchor location of a given context is only considered if its OpenPGP fingerprint matches one of the fingerprints from the set.

If the set is empty (the built-in default), no filtering based on fingerprints occurs and any valid OpenPGP certificate found in the trust anchor location of a given context is considered.

“web_of_trust” verification method object

The Web of Trust[9] verification method for OpenPGP signatures is a flexible form of trust based verification, which relies on the Berblom algorithm[10]. It considers both artifact verifiers and trust anchors. With it, each artifact verifier must reach a configurable amount of trust to be considered for the verification of OpenPGP signatures. Here, the trust amount is calculated by a “Web of Trust” network, which relies on implicit or explicit trust roots and configurable trust amounts for partially trusted introducers in the network.

To constrain the set of valid OpenPGP certificates allowed as artifact verifier, it can be filtered by domain name matches against valid OpenPGP User IDs. To constrain the set of valid OpenPGP certificates allowed as trust anchor, it can be filtered by OpenPGP fingerprints.

“flow_amount” number

The optional trust amount required for a flow in a “Web of Trust” network to be considered “fully trusted”. This signifies the target trust amount that an artifact verifier must reach for it to be considered for the verification of OpenPGP signatures.

If set, the value must be a non-zero, positive integer (built-in default: 120).

“partial_amount” number

The optional trust amount for a partially trusted introducer in a “Web of Trust” network. This signifies the trust amount of an OpenPGP certificate considered on a path between trust anchor and artifact verifier.

If set, the value must be an integer between 1 and 120 (built-in default: 40).

“roots” set of objects

An set of trust roots for the “Web of Trust. Each object in this set signifies an OpenPGP certificate that is considered as trust root, alongside its specific trust amount.

Each trust root is an object with the following two fields:

  • “fingerprint”: An OpenPGP fingerprint.

  • “amount”: The optional trust amount assigned to the trust root. If set, the value must be an integer between 1 and 120 (built-in default: 120).

    NOTE: If this field is omitted, a concrete value is derived from built-in defaults.

If the set has entries, a valid OpenPGP certificate found in the trust anchor location of the VOA hierarchy for a given context is considered as trust root with the specific trust amount if its OpenPGP fingerprint matches that of a “fingerprint” in the set.

If the set is empty, any valid OpenPGP certificate found in the trust anchor location of the VOA hierarchy for a given context is considered as trust root with a trust amount of 120.

“artifact_verifier_identity_domain_matches” set

An optional set of valid domain names, that is used to filter artifact verifiers.

If the set has entries, a valid OpenPGP certificate found in the artifact verifier location of a given context is only considered if one of its valid OpenPGP User IDs matches one of the domain names from the set and it has the required target trust amount (see “flow_amount”).

If the set is empty, no filtering based on domain names occurs and any valid OpenPGP certificate found in the artifact verifier location which has at least one OpenPGP User ID with the required target trust amount (see “flow_amount”) is considered.

“contexts” list of objects

A list of zero or more objects, that each provide a context-level override to the technology settings imposed by the OS (if any) or the built-in defaults.

WARNING: For the voa file as a whole to be considered valid, the “default_technology_settings” object must be defined, if this list is empty or the field is not set!

“purpose” string

The VOA purpose of the OS, that is targeted. Refer to the VOA specification for details on the allowed character set.

“context” string

The VOA context of the OS, that is targeted. Refer to the VOA specification for details on the allowed character set.

“technology_settings” object

Technology settings for the “purpose” and “context” of the OS targeted by the configuration file name (or drop-in directory name).

The settings in this object override the less specific OS-level or built-in technology settings. Refer to the “default_technology_settings” section for an overview of the expected object.

EXAMPLES

Defaults

The below represents the built-in defaults for technology settings.

default_technology_settings:
  openpgp:
    num_data_signatures: 1
    verification_method:
      trust_anchor:
        required_certifications: 3
        artifact_verifier_identity_domain_matches:
        trust_anchor_fingerprint_matches:

The configuration targets the basic “trust anchor” method for OpenPGP, requiring a single data signature for artifacts and at least three certifications on artifact verifiers from trust anchors.

The set of domain name matches for artifact verifiers and OpenPGP fingerprint matches for trust anchors are left empty on purpose, which means that no specific restrictions on those verifiers are imposed.

OpenPGP “plain” verification method

The following configuration example targets the “plain” verification method for OpenPGP.

default_technology_settings:
  openpgp:
    num_data_signatures: 1
    verification_method:
      plain:
        identity_domain_matches:
          - example.org
          - sub.example.org
        fingerprint_matches:
          - d3b0f7c0b825ecbb0f0d7398072947e7b1537b6f
          - e242ed3bffccdf271b7fbaf34ed72d089537b42f
          - f1d2d2f924e986ac86fdf7b36c94bcdf32beec15

Assuming that the above contents reside in the file “/usr/share/voa/example.yaml”, for the OS “example” the following verification rules apply:

A single data signature from a valid artifact verifier is required for artifacts to be verified. Further, the following restrictions are imposed on OpenPGP certificates found in the artifact verifier location of a specific context to be considered valid:

  • A valid OpenPGP User ID must match either the domain “example.org” or “sub.example.org”.
  • The OpenPGP fingerprint must match “d3b0f7c0b825ecbb0f0d7398072947e7b1537b6f”, “e242ed3bffccdf271b7fbaf34ed72d089537b42f”, or “f1d2d2f924e986ac86fdf7b36c94bcdf32beec15”

OpenPGP basic “trust anchor” verification method

The following configuration example targets the basic “trust anchor” verification method for OpenPGP.

default_technology_settings:
  openpgp:
    num_data_signatures: 1
    verification_method:
      trust_anchor:
        required_certifications: 3
        artifact_verifier_identity_domain_matches:
          - example.org
          - sub.example.org
        trust_anchor_fingerprint_matches:
          - d3b0f7c0b825ecbb0f0d7398072947e7b1537b6f
          - e242ed3bffccdf271b7fbaf34ed72d089537b42f
          - f1d2d2f924e986ac86fdf7b36c94bcdf32beec15

Assuming that the above contents reside in the file “/usr/share/voa/example.yaml”, for the OS “example” the following verification rules apply:

A single data signature from a valid artifact verifier is required for artifacts to be verified. Each artifact verifier must be certified by three trust anchors.

A valid OpenPGP User ID of a valid OpenPGP certificate found in the artifact verifier location of a specific context must match either the domain “example.org” or “sub.example.org” for the certificate to be considered as artifact verifier.

The OpenPGP fingerprint of a valid OpenPGP certificate found in the trust anchor location of a specific context must match either “d3b0f7c0b825ecbb0f0d7398072947e7b1537b6f”, “e242ed3bffccdf271b7fbaf34ed72d089537b42f”, or “f1d2d2f924e986ac86fdf7b36c94bcdf32beec15” for the certificate to be considered as trust anchor.

OpenPGP “Web of Trust” verification method

The following configuration example targets the “Web of Trust” verification method for OpenPGP.

default_technology_settings:
  openpgp:
    num_data_signatures: 1
    verification_method:
      web_of_trust:
        flow_amount: 120
        partial_amount: 40
        roots:
          - fingerprint: d3b0f7c0b825ecbb0f0d7398072947e7b1537b6f
            amount: 120
          - fingerprint: e242ed3bffccdf271b7fbaf34ed72d089537b42f
            amount: 120
          - fingerprint: f1d2d2f924e986ac86fdf7b36c94bcdf32beec15
            amount: 120
        artifact_verifier_identity_domain_matches:
          - example.org
          - sub.example.org

Assuming that the above contents reside in the file “/usr/share/voa/example.yaml”, for the OS “example” the following verification rules apply:

A single data signature from a valid artifact verifier is required for artifacts to be verified.

Each partially trusted introducer in the “Web of Trust” network is assigned a trust amount of 40. The trust amount required for a valid OpenPGP certificate found in the artifact verifier location of a given context to be considered “fully trusted” and thus for it to be considered as artifact verifier is 120.

The OpenPGP fingerprint of a valid OpenPGP certificate found in the trust anchor location of a specific context must match either “d3b0f7c0b825ecbb0f0d7398072947e7b1537b6f”, “e242ed3bffccdf271b7fbaf34ed72d089537b42f”, or “f1d2d2f924e986ac86fdf7b36c94bcdf32beec15” for the certificate to be considered as trust anchor. Each of these matching trust anchors is assigned a trust amount of 120.

OS-level and context-level

The below represents an example of OS and context-level technology settings relying on the basic “trust anchor” verification method for OpenPGP. It is assumed that the contents reside in the file “/usr/share/voa/example.yaml”, thus targeting the OS “example”.

default_technology_settings:
  openpgp:
    num_data_signatures: 2
    verification_method:
      trust_anchor:
        artifact_verifier_identity_domain_matches:
          - example.org
        trust_anchor_fingerprint_matches:
          - 6132b58967cf1ebc05062492c17145e5ee9f82a8
          - 6eadeac2dade6347e87c0d24fd455feffa7069f0
          - b787a81c32997fd39a5f4c0188363902d3586e7b
          - d3b0f7c0b825ecbb0f0d7398072947e7b1537b6f
          - e242ed3bffccdf271b7fbaf34ed72d089537b42f
contexts:
  - purpose: package
    context: default
    technology_settings:
      openpgp:
        verification_method:
          trust_anchor:
            required_certifications: 4
            artifact_verifier_identity_domain_matches:
              - packages.example.org
  - purpose: image
    context: installation-medium
    technology_settings:
      openpgp:
        num_data_signatures: 1
        verification_method:
          trust_anchor:
            artifact_verifier_identity_domain_matches:
              - install.example.org

If a system using this configuration file does not have any other voa file, the built-in defaults apply for any unset fields in the OS-level technology settings and the OS-level technology settings apply for any unset fields in the context-level technology settings.

The above example illustrates, how OS-level defaults are handed down, if they are not set in the more specific context-level settings. For example, the “trust_anchor_fingerprint_matches” in the OS-level defaults also apply to the context-level technology settings.

Relatedly, fields in context-level technology settings can override OS-level defaults. In the above example, the “num_data_signatures” are set to 2 on the OS level, but one of the contexts overrides it to require only 1. Similarly, the “required_certifications” in one of the contexts is overridden to 4 instead of the OS-level value of 3 (itself derived from the built-in defaults).

Drop-ins for context-level overrides

When only providing context-level settings, the top-level “default_technology_settings” key can be omitted.

The following configuration example targets the “plain” verification method for OpenPGP. It is assumed that the contents reside in the file “/etc/voa/example.yaml.d/90-my-repo.yaml”, targeting the OS “example”.

contexts:
  - purpose: package
    context: my-repo
    technology_settings:
      openpgp:
        num_data_signatures: 1
        verification_method:
          plain:
            identity_domain_matches:
              - other.org
            fingerprint_matches:
              - bea43e7033e19327183416f23fe2ee1b64c25f4a

Assuming that also the configuration described in the OS-level and context-level example is present on the system, this configuration describes a specialization for a single package repository named “my-repo”. Technology settings enforced by the OS “example” are overridden and a different set of verification rules apply:

  • Only one data signature from valid artifact verifiers is required for each artifact.
  • The following restrictions are imposed by filters on the verifiers:
    • Valid OpenPGP certificates found in relevant locations are only considered as artifact verifiers, if their OpenPGP fingerprint matches “bea43e7033e19327183416f23fe2ee1b64c25f4a”.
    • Valid OpenPGP certificates found in relevant locations are only considered as artifact verifiers, if one of their valid OpenPGP User IDs matches the domain name “other.org”.

SEE ALSO

voa(1)

NOTES

  1. VOA

    https://uapi-group.org/specifications/specs/file_hierarchy_for_the_verification_of_os_artifacts/

  2. Configuration Files Specification

    https://uapi-group.org/specifications/specs/configuration_files_specification/

  3. XDG Base Directory Specification

    https://specifications.freedesktop.org/basedir/latest/

  4. OpenPGP signature

    https://openpgp.dev/book/signatures.html

  5. OpenPGP certificate

    https://openpgp.dev/book/certificates.html

  6. OpenPGP User ID

    https://openpgp.dev/book/certificates.html#user-ids

  7. OpenPGP fingerprint

    https://openpgp.dev/book/certificates.html#fingerprint

  8. OpenPGP third-party certifications

    https://openpgp.dev/book/signing_components.html#third-party-certifications

  9. Web of Trust

    https://codeberg.org/openpgp/wot

  10. Berblom algorithm

    https://codeberg.org/Nukesor/berblom

API documentation

Local Dependency Graph

The following figure shows the dependency relationships between the crates of the VOA workspace.

Dependency Graph

Contributing

These are the contributing guidelines for the voa project.

Development takes place at https://gitlab.archlinux.org/archlinux/alpm/voa.

Writing code

This project is written in Rust and formatted using the nightly rustfmt version. The linking is performed via mold.

All contributions are linted using clippy and spell checked using codespell. The dependencies are linted with cargo-deny and unused dependencies are detected using cargo-machete. License identifiers and copyright statements are checked using reuse. Toml files are formatted via [taplo-cli].

Various just targets are used to run checks and tests. shellcheck is used to check the just targets for correctness. In order to review the snapshot changes in tests, you can use cargo-insta.

Code examples in READMEs is tested via tangler. Links in markdown files or doc blocks are tested via lychee.

To get all of the necessary tools installed on Arch Linux, run just install-pacman-dev-packages. To setup Rust for this project run just install-rust-dev-tools. Both can also be done in one fell swoop via just dev-install.

To aide in development, it is encouraged to configure git to follow this project’s guidelines:

just configure-git

This just command takes care of a few things:

Testing

We run nextest for fast execution of unit and integration tests. just test calls cargo nextest as well as just test-docs, which runs the doc tests as nextest isn’t capable of doing that yet.

The just test-coverage command creates a cobertura code coverage report and an HTML report. Both are written to target/llvm-cov/, which utilizes the llvm-tools-preview component. The just test-coverage doc additionally includes doc tests into the coverage report. However, this is still an unstable nightly-only feature.

The just containerized-integration-tests recipe executes all tests that are made available by a _containerized-integration-test feature and are located in an integration test module named containerized. With the help of a custom target runner, these tests are executed in a containerized environment using podman.

The parameter --nocapture shows output of the tests. Passing test names as a parameter causes only that subset of tests to be run.

Writing commit messages

To ensure compatibility and automatic creation of semantic versioning compatible releases the commit message style follows conventional commits.

The commit messages are checked by just run-pre-push-hook via the following tools: cocogitto & committed.

Follow these rules when writing commit messages:

  1. The subject line should be capitalized and should not end with a period.

  2. The total length of the line should not exceed 72 characters.

  3. The commit body should be in the imperative mood.

  4. Avoid using the crate name as the commit scope. (e.g. feat(voa-core)) The changelog entries will be generated for the associated crate accordingly using release-plz and git-cliff.

Here is an example of a good commit message:

feat(parser): Enhance error handling in parser

Improve error handling by adding specific error codes and messages
to make debugging easier and more informative. This update enhances
parsing accuracy and provides more context for handling parsing errors.

Signed-off-by: John Doe <john@archlinux.org>

Merging changes

Changes to the project are proposed and reviewed using merge requests and merged by the developers of this project using fast-forward merges.

Creating releases

Releases are created by the developers of this project using release-plz by running (per package in the workspace):

just prepare-release <package>

Changed files are added in a pull request towards the default branch.

Once the changes are merged to the default branch a tag is created and pushed for the respective package:

just release <package>

The crate is afterwards automatically published on https://crates.io using a pipeline job.

License

All code contributions fall under the terms of the Apache-2.0 and MIT.

Configuration file contributions fall under the terms of the CC0-1.0.

Documentation contributions fall under the terms of the CC-BY-SA-4.0.

Specific license assignments and attribution are handled using REUSE.toml. Individual contributors are all summarized as “VOA Contributors”. For a full list of individual contributors, refer to git log --format="%an <%aE>" | sort -u.

Security Policy

Supported Versions

We release patches for security vulnerabilities. Currently, only the most recent release of a crate is eligible for receiving such patches.

Reporting a Vulnerability

Please report (suspected) security vulnerabilities on the issue tracker as a confidential issue with a description of the issue, the steps you took to create the issue, affected versions, and, if known, mitigations for the issue.

We will respond as quickly as possible after you open an issue. However, please note that this project is maintained by volunteers, so response times may vary. If the issue is confirmed as a vulnerability, we will open a Security Advisory and acknowledge your contributions as part of it.

This project follows a coordinated vulnerability disclosure (CVD) with a 90-day disclosure policy, meaning reported vulnerabilities will be addressed within 90 days before public disclosure.

Media

Videos

2025: Verification of OS artifacts without stateful keyrings (All Systems Go!)

Slides: https://pkgbuild.com/~dvzrv/presentations/all-systems-go-2025/