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

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.