Sealing and manifests¶
For campaign owners who need a finished project to stay provably unchanged.
A project carries two signed integrity artifacts, both built from the same deterministic record of what the tree contained at one moment, and both signed over a domain-separated Ed25519 digest so one can never be replayed as the other:
A manifest is a signed snapshot of every file in the tree. It answers does this tree still match what was signed? It lives at
httk_project/manifest.jsonl.bz2.A seal is a signed statement that additionally records, for every project member, the digest of that member’s own seal — so a project seal transitively pins whole member subtrees without re-hashing them. It lives at
httk_project/seal.json.
Neither is encryption or access control: both are public and detached. What they buy is detection. Any later change to a covered byte becomes a discrepancy the moment the artifact is verified.
The record list¶
Both artifacts are built from one canonical record list: one entry per file,
symlink, and directory, sorted by path, never following symlinks. A file record
carries its size, SHA-256, and owner-execute bit, so a runner cannot be quietly
made (un)runnable. httk.core.records.file_records produces it; two seams keep
out what must not be covered — fnmatch exclusion patterns on posix relpaths,
and a skip predicate on absolute paths.
What a manifest excludes: the anchor material it cannot authenticate (the trust
anchors in project.json, private keys, remote credentials, and the manifest
file itself), plus whatever each member’s handler names in its
manifest_exclusions — its own control and scratch directories, while its
payload stays covered. A seal excludes all of that and each member subtree as a
whole, because a member subtree is covered through its seal digest instead.
Verdicts¶
Verifying either artifact answers two independent questions — does it still describe this tree and was it made by a key this project trusts — and reports both as one of three verdicts:
Verdict |
Meaning |
Exit |
|---|---|---|
|
the signature verifies and a signer is a pinned trust anchor |
0 |
|
the signature verifies, but nothing pins the signer |
3 |
|
the artifact no longer describes the tree, or a signature does not verify |
1 |
The trust anchor is the key pinned in project.json — never the key an artifact
carries in its own header. The project’s own signing seed lives inside the tree,
so anybody who can write the tree can re-sign it; a pin is what makes a signature
mean who signed, not merely that it is self-consistent.
The manifest¶
httk project manifest create # write httk_project/manifest.jsonl.bz2
httk project manifest verify # verify it against the tree
httk project manifest verify --trusted-key keys/collaborator.pub
verify prints valid/invalid, then a <verdict>: <reason> line, and exits
with the verdict’s code. The programmatic equivalents are create_manifest and
verify_manifest in httk.core.project.
Key refs¶
A seal is signed by one or more keys, each named by a ref:
Ref |
Signs with |
|---|---|
|
the project’s own signing seed, discovered from the tree |
|
the default operator identity |
|
a named operator identity |
a path |
a base64 Ed25519 seed file |
The default is the project’s seal_keys member, or project,identity when it is
unset. seal --keys REFS overrides it for one call. A ref that cannot be
resolved is skipped with a warning rather than failing the seal; only resolving
no key at all is an error.
Sealing, unsealing, and verifying¶
httk project seal # seal loose files + every member's digest
httk project seal --keys project,identity
httk project verify-seal # verify the project and every member seal
httk project verify-seal --shallow # verify only the project seal
httk project unseal # remove the seal (prompts; --force to skip)
Every registered member must already be sealed, and every member kind must have a
registered handler; seal refuses otherwise, naming what is wrong. Registering
or moving a member is refused while the project is sealed — a seal a project
commits to must not change beneath it — so unseal first.
verify-seal prints one line per entry — <level> <subject> <verdict> <reason>
— with indented <kind> <path> discrepancy lines beneath any failing entry, then
a final ok / UNTRUSTED / FAILED word whose exit code matches the table
above. By default the project’s pinned keys and the local identity’s public key
are trusted, so a tree sealed by its own project or identity verifies as
valid_trusted without naming a key; --trusted-key adds more, as an ed25519:
key, a sha256: fingerprint, or a *.pub file. --json prints
{ "entries": [...], "ok": ..., "trusted": ... }.
Not to be confused with¶
httk project export. The command that packages a project for distribution
as a signed ZIP is an export; seal here means only the integrity seal
described in this document.