holy-package(5)
NAME
holy-package - prototype native archive and payload manifest
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.
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.
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.
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.
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.
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.
The prototype verifier reads HOLY/files. For each regular payload file under DATA/, one manifest line contains twelve whitespace-separated tokens:
file PATH MODE OWNER GROUP UID GID SIZE SHA256 FLAGS XATTRS HARDLINK-GROUP
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
SEE ALSO
holypkg (8),holy.conf (5)