_ _
| |_ ___| |_ _
| | . | | | |
|_|_|___|_|_ |
|___|
git mirror - github.com/owenewans/holy - branch master
file man/holy-package.5
.TH HOLY-PACKAGE 5 "September 2026" "Holy" "File Formats"
.SH NAME
holy-package \- prototype native archive and payload manifest
.SH DESCRIPTION
A .holy archive is a POSIX tar/PAX stream compressed as a standard LZ4 frame.
HOLY/meta contains one key and one value per line. The required fields are
format (holy-package-1), name, version, release, os, arch and libc. An unknown
requires-feature field fails inspection. Optional x-version-family records the
version comparator family and is a scalar: duplicates fail inspection.
The current resolver supports constrained package requirements for pacman,
Debian and explicitly tagged Holy native versions. It compares versions
within the consumer's declared family;
unknown families remain inspectable but cannot acquire guessed version semantics.
Unversioned file requirements match exact nondirectory paths in verified
payload manifests. Declared file claims alone do not establish file ownership.
Unversioned command requirements match executable payloads in the standard
bin directories. Links must resolve to an executable in the same artifact;
declared command claims alone do not prove one exists.
Native packages opt in with x-version-family holy. Their version and release
use decimal components separated by dots, with an optional lowercase
prerelease suffix introduced by hyphen or tilde, such as 7.3-rc4. Invalid
Holy native version or release fields fail inspection. Native comparison
treats absent trailing numeric components as zero, compares numbers without
fixed-width overflow and places prereleases before final versions.
Pacman imports store the complete foreign epoch:version-release in version,
and use release for the native artifact revision.
.PP
The inspector accepts os=linux or windows, arch=x86, x86_64 or noarch,
and libc=glibc, musl or nolibc. noarch requires nolibc. These checks
validate declared metadata values; they do not scan ELF files to prove ABI
or prove that a purported noarch payload has no machine code.
.PP
The separate holypkg scan command examines regular ELF payload files and each
hardlink path, and
rejects known arch/libc tag mismatches. The basic info and verify commands
do not perform this ABI check.
.PP
The scanner accepts ET_EXEC without PT_INTERP and PT_DYNAMIC as nolibc,
and ET_DYN requiring libc.so.6 as glibc evidence, including its basename in
literal DT_NEEDED paths. Architecture-specific musl loader or libc names and
the native libc.musl-x86_64.so.1 / libc.musl-i386.so.1 SONAMEs provide musl
classification evidence. The native libc.so.6 and matching glibc loader
SONAMEs classify those glibc runtime files. A generic libc.so name does not.
Foreign import writes a SONAME capability only after reading a classified
ET_DYN payload file and records that file as evidence. It rejects ELF files
whose runtime remains unknown. A plugin without such evidence needs
classification and review before a package can pass scan.
.PP
The scanner accepts an ET_REL kernel module only under
usr/lib/modules/RELEASE/kernel/ with a .ko suffix, matching package
architecture, libc=nolibc and a .modinfo vermagic release matching its path.
It does not treat module symbols as userspace providers. Other ET_REL objects
remain unknown for ABI classification.
.PP
The archive contains one regular file at each of HOLY/meta, HOLY/files,
HOLY/deps, HOLY/provides, HOLY/hooks, HOLY/origin and HOLY/transform. The
inspector checks their presence and type. Empty metadata placeholders can pass
this structural check; it does not validate dependency, capability, hook or
origin content. The separate requirements and provides commands parse
supported subsets of HOLY/deps and HOLY/provides.
.PP
The prototype verifier reads HOLY/files. For each regular payload file under
DATA/, one manifest line contains twelve whitespace-separated tokens:
.nf
file PATH MODE OWNER GROUP UID GID SIZE SHA256 FLAGS XATTRS HARDLINK-GROUP
.fi
MODE is octal; UID, GID and SIZE are decimal. PATH is relative to DATA. SHA256
has 64 hexadecimal digits. FLAGS accepts none, config, mutable or
config,mutable for regular files. The verifier rejects flags on directories,
symlinks and hardlinks. Installed checks still compare mutable files with their
manifest; update policy for their local changes is not implemented. XATTRS=-.
Independent files use HARDLINK-GROUP=-. A symlink uses one additional TARGET token,
SIZE=0 and SHA256=-. A hardlinked file uses TYPE=hardlink, a group identifier
in HARDLINK-GROUP and one extra TARGET token naming a regular file in DATA.
The target file carries the same group identifier. SIZE and SHA256 describe
the target content. The verifier rejects chained hardlinks and checks mode,
UID and GID against the target. A directory uses the same twelve tokens as a file, with
TYPE=dir, SIZE=0 and SHA256=-. The verifier rejects symlink targets that
escape the root by lexical traversal. The text lexer follows holy.conf(5), so
quotes and escapes can preserve spaces in paths. Every archived directory
except DATA itself needs a manifest record. The verifier does not support
special files, ACLs or extended attributes. The verifier rejects DATA entries
that carry archive xattrs or ACLs, even if HOLY/files says XATTRS=-. The
planned full format includes those attributes.
.PP
The narrow database apply command installs approved data, native static ELF
files and relative symlinks into existing or explicitly declared safe directories. Symlinks
require mode 0777 and the caller's numeric ownership. The transaction records
their targets, checks them without following links, and removes only intact
owned entries. Direct hardlinks share the verified regular target's inode; one
regular anchor belongs to each named group. Installation defers hardlinks until
regular payload files exist, independently of archive order. Absolute symlink
installation remains unsupported. Cached updates preserve hardlink groups across
content and mode changes, member additions/removals, anchor moves and group
splits/merges. The file plan names a reserved staging link for each new group;
recovery validates both content and inode relationships.
The pack command writes and verifies
this archive format from a prepared tree of regular files, directories, symlinks
and hardlinks. It selects a deterministic regular anchor for each inode group;
manifest generate uses the same selection.
The verifier does not verify dependencies,
signatures, symbolic owner names or the installed filesystem. A full format
implementation must verify file ownership, links, xattrs, hooks, origin and
transaction semantics before accepting an artifact for installation.
.SH FOREIGN IMPORT RECORDS
The pacman and Debian importers preserve original package metadata beneath
HOLY/foreign/pacman and HOLY/foreign/deb. HOLY/origin binds these records to the exact original archive
hash and retains original field values and line numbers. The source-name field
is provenance, not authority to assign a trusted installed source-id.
.PP
A requirement of kind foreign preserves an unsupported source expression. The
current solver reports unsupported semantics instead of dropping that requirement.
Foreign hooks may be skipped by an explicit artifact-scoped --skip-hooks decision
during set installation. The installed package then carries hooks-state with the
hash of HOLY/hooks and reports installed-unconfigured in check. Native
postinstall records can be reviewed and executed through db configure-plan and
db configure-apply. A completed hooks-state retains the same hash. Foreign
script execution, preinstall, editing and service consent remain unimplemented.
For a reviewed update preserving a modified config, the installed instance keeps
the exact archived HOLY/files as package-files. Its files record describes the
preserved public file and the new PATH.holy-new. config-state binds the source
artifact, raw manifest hash, installed manifest hash and transaction plan hash.
The archive digest and local installed manifest digest remain distinct.
HOLY/transform records
changes already applied to the normalized payload; installation never executes
that record. Archive verification does not authorize hook execution.
.PP
The AppImage converter records family appimage with the converter
holy-appimage-1, the original image hash, the entry point and mode extract in
HOLY/origin. Its payload keeps the whole extracted AppDir under
/usr/lib/holy/private/NAME/appdir/, a link to it at
/usr/lib/holy/private/NAME/usr/bin/NAME.appimage and a generated launcher at
/usr/bin/NAME; the launcher is a script, so the package names an interpreter
requirement like any other script. HOLY/transform records the private layout,
the launcher, the rewritten desktop entry, the path view a link needs, the
dropped image-level sandbox and the absence of a desktop database hook. The
version, arch and libc are read from the payload rather than assumed, and a
converted package therefore carries the requirements that payload implies.
.PP
The Snap converter records family snap with the converter holy-snap-1, the
original image hash, the manifest version, the command and mode extract in
HOLY/origin. Its payload keeps the whole snap root under
/usr/lib/holy/private/NAME/snap/ with the image at
/usr/lib/holy/private/NAME/usr/bin/NAME.snap and a generated launcher at
/usr/bin/NAME; the launcher is a script, so the package names an interpreter
requirement like any other script. HOLY/transform records the private layout, the
launcher, the base requirement, the dropped confinement, each dropped plug, and the
recorded hooks, environment names and command-chain entries. The base the manifest
names becomes a package requirement on that name, because the runtime a snap store
mounts has to come from a source on this system; a superblock states no runtime, so
that requirement names no libc. The name, version, arch and libc are read from the
manifest and the payload rather than assumed.
.PP
The Scoop importer records family scoop with the converter holy-scoop-1, the
manifest digest, the artifact URL, its digest and mode artifact in HOLY/origin. Its
payload carries the verified artifact whole under /usr/lib/holy/private/NAME/, with
the directory the manifest declares as extract_dir replacing the first component of
every archive member path, and the manifest copy sits in the output directory beside
the package. HOLY/transform records the private path, the extract_dir, the program
the manifest names and that the installer was dropped without a Wine requirement.
Each dependency of the manifest becomes one package requirement on the app name, and
HOLY/hooks stays empty because nothing runs at install.
.PP
The WinGet importer records family winget with the converter holy-winget-1 through the
same writer, so its payload, its private path, its transform record and its empty
HOLY/hooks match the Scoop package: only the manifest fields, the requirement ids and
the report wording name the catalog instead of the bucket.
.PP
The Nix closure importer records family nix with the converter holy-nix-1, the
capture digest, the store path, its hash and mode closure in HOLY/origin. It emits one
package per store path, each keeping its own store path whole under
/usr/lib/holy/private/NAME/store/STORE_PATH/. A store path that states an entry point
also carries a link at /usr/lib/holy/private/NAME/usr/bin/NAME and a generated
launcher at /usr/bin/NAME, so the package names an interpreter requirement like any
other script. HOLY/transform records the private layout, the launcher, the absent store
view, the dropped store daemon and the reference count. Each reference the capture
declares to a store path it carries becomes one package requirement on that name; a
reference to a store path it does not carry becomes a requirement whose original field
is the store hash and which names no architecture or runtime. Nix states no version,
so every package records version 0 and the store hash is its identity in
x-nix-store-hash. HOLY/hooks stays empty because nothing runs at install.
.PP
The Solus eopkg importer records family eopkg with the converter holy-eopkg-1, the
artifact digest, the original version, the original architecture, the PiSi format,
the install tar digest and mode artifact in HOLY/origin, and it writes
x-eopkg-format, x-source-arch and x-eopkg-component into the metadata. The install
tar travels whole under /usr/lib/holy/private/NAME/eopkg/, since a Solus layout is
recorded rather than claimed. HOLY/transform records the private layout, the install
tar digest, the dropped install script and COMAR object, the dropped delta and the
untrusted signature. Each runtime dependency becomes one exact package requirement
whose original field is the releaseFrom value the metadata states, or - when it
states none, since a distribution release is a property of the repository rather than
of the dependency. A SONAME the payload needs and does not provide becomes a SONAME
requirement, and the package name is provided as a package capability. HOLY/hooks
stays empty because nothing runs at install.
.SH INSTALLED GRAPH
New local installations use holy-instance-4 state, or holy-instance-5 when an
explicit non-native architecture placement decision was accepted. Version 5 adds
an architecture record with the reviewed host and unchanged target. The decision
is scoped to that artifact hash and does not certify execution. The state includes a graph
SHA-256; the graph file uses holy-resolution-1 with scope artifact-candidates,
a root artifact, sorted selected artifact hashes and selected requirement edges.
Each edge records consumer hash, requirement ID, provider hash, path, kind and
target using the holy.conf lexer. The plan hash includes these canonical bytes.
.PP
Database operations reject a missing, modified, writable or symlink graph.
Legacy holy-instance-1 entries remain readable without a graph. Their absence
of recorded provider choices does not establish dependency correctness.
.PP
Source-associated installations also preserve the source fields introduced in
holy-instance-3. State
records the registered source-id and SHA-256 of a separate source record containing
that ID and the alias at installation. The graph and reason fields retain their
version-2 semantics. The source record is manager-owned state, not copied from
the package. The database rejects absent, changed, writable or symlink source
records and disagreement between the record and state. Renaming or deactivating
the registered source does not rewrite installed origin records. Check, removal,
orphan analysis and missing-only repair preserve this identity without requiring
the source to remain active.
.PP
New records include a provides file copied from HOLY/provides and bind its
SHA-256 in state. Empty provides is valid. Missing, modified, writable or symlink
provides records invalidate the database. Installed package aliases can be
located from these records; read-only database checks do not require the archive
cache. Constructing an installation plan still requires verified cached artifacts.
Legacy holy-instance-1/2/3 records remain readable. Alias discovery for a legacy
record reads its verified cached artifact; missing cache returns unavailable
instead of silently treating unknown capabilities as absent.
.PP
The set installation interface supports package names and declared aliases,
scoped pacman version constraints and native
dynamic packages with literal absolute interpreter and DT_NEEDED provider paths.
It records explicit/dependency reasons. General loader search, automatic ELF
rewriting remains unimplemented. Slot conflicts compare source-id, name, os,
arch and libc; distinct artifacts in different slots may coexist with compatible
file ownership. Reviewed cached update plans can replace one source-bound slot;
registering the same artifact in multiple slots remains unsupported. Missing-only cache repair
preserves the installed graph and refuses changed or partial files.
.SH SEE ALSO
.BR holypkg (8),
.BR holy.conf (5)