# VOA
A command line interface and library for interacting with the "File Hierarchy for the **V**erification of **O**S **A**rtifacts" ([VOA]).
## Documentation
- <https://voa.archlinux.page/rustdoc/voa/> for development version of the crate
- <https://docs.rs/voa/latest/voa/> for released versions of the crate
## 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]:
```bash no_run
cargo install --locked --features cli voa
```
When using the source repository of the project, the path has to be specified:
```bash no_run
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:
```bash no_run
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:
```bash no_run
just generate shell_completions voa
```
The shell completion files are placed into `output/shell_completions/` by default.
## Examples
### Library
#### Import and search
```rust
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:
```rust no_run
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`.
```bash
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.
```bash
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.
```bash
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
# "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.
```bash
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
# "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.
```bash no_run
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.
[Apache-2.0]: ../LICENSES/Apache-2.0.txt
[MIT]: ../LICENSES/MIT.txt
[OpenPGP certificate]: https://openpgp.dev/book/certificates.html
[Rust]: https://rust-lang.org/
[VOA]: https://uapi-group.org/specifications/specs/file_hierarchy_for_the_verification_of_os_artifacts/
[contribution guidelines]: ../CONTRIBUTING.md
[signing capabilities]: https://openpgp.dev/book/certificates.html#defining-operational-capabilities-of-component-keys-with-key-flags
[voa-config]: https://voa.archlinux.page/voa-config/