httk.core.project

The httk project anchor and the core-owned httk project command.

A project is a directory marked at its root by a httk_project control directory, discovered by walking upward exactly as git finds a .git. This package owns that anchor — creating it, reading and validating its project.json, and managing its Ed25519 identity and trust anchors — and the core-only httk project command that operates on it. It creates no workflow workspace; that is layered on by a workflow installation.

The anchor API is re-exported here for convenience; the command and its implementation live in httk.core.project.cli.

Submodules

Attributes

PROJECT_DIRECTORY

PROJECT_FILE

PUBLIC_KEY_PREFIX

PROJECT_PRIVATE_KEY_RELATIVE_PATH

The exact private-key path created by the project identity implementation.

DEFAULT_MANIFEST_EXCLUSIONS

Exceptions

LegacyProjectError

Raised when discovery finds an httk v1 ht.project directory.

SealedError

An action was refused because the subject, or its enclosure, is sealed.

SealError

A seal cannot be written or verified.

Classes

ManifestVerification

What verifying one manifest against one tree established.

ProjectMember

One member a project holds: a subtree of a given kind.

ProjectMemberHandler

What core needs from the module that owns one member kind.

Discrepancy

One way a sealed subject no longer matches its seal.

Seal

One parsed seal document.

SealKeys

The signing keys resolved for one seal, and the roles that were missing.

SealReport

The verdicts of verifying a seal and, when deep, every seal below it.

SealVerification

What verifying one seal established.

Functions

canonical_public_key(value)

Normalize any accepted public key spelling to the recorded one.

discover_project([start])

Find the nearest project root, or refuse a legacy one, at or above start.

format_public_key(raw)

Return the recorded representation of one raw Ed25519 public key.

import_v1_project(root, *[, source, name])

Create the project anchor from a legacy ht.project directory.

initialize_project(root, *, name[, description, ...])

Initialize the project anchor: its metadata, its key, and its remotes dir.

key_fingerprint(value)

Return the stable display fingerprint of one public key.

parse_public_key(value)

Decode a recorded public key, accepting the bare base64 spelling too.

pin_project_key([root])

Adopt the project's current keys/project.pub as its trust anchor.

pinned_project_key(metadata)

Return the project's own pinned public key, or None when absent.

project_public_key_path(root)

Return where a project keeps its own signing key's public half.

read_project(root)

Read and validate the project.json of the project rooted at root.

read_project_section(root, name)

Return one named object member of project.json, empty when absent.

read_public_key_file(path)

Read one public-key file and return its recorded public key.

require_project([start])

Return the nearest project root, refusing when there is none.

trust_project_key(root, key)

Adopt one further public key as a trust anchor of this project.

trusted_project_keys(metadata)

Return every key metadata pins: the project's own and any adopted one.

write_project_section(root, name, value)

Store one named object member of project.json and return the metadata.

export_project(out_path[, project_path])

Create a signed ZIP containing a project without private keys.

verify_export(zip_path, *[, expect_key, trusted_keys])

Verify an exported project ZIP and return signer information.

create_manifest([project, output])

Create and atomically publish the signed v2 project manifest.

project_exclusions(metadata)

Return the default manifest exclusions plus the project's configured ones.

resolve_trusted_keys([project, trusted_keys])

Return the trust anchors of project plus every explicitly named key.

verify_manifest([project, manifest, trusted_keys])

Verify a project's v2 manifest against the tree and its trust anchors.

members_path(project_root)

Return where a project's member registry lives.

project_members(project_root)

Return the members registered in a project, in recorded order.

register_project_member(project_root, path, kind, *[, ...])

Record that a member of kind lives at path, idempotently.

set_project_member_name(project_root, path, name)

Set (or clear) the recorded name of the member at path.

unregister_project_member(project_root, path)

Remove the member recorded at path.

update_project_member_path(project_root, old, new)

Move the member recorded at old to new, keeping its kind.

build_seal_body(kind, subject, records)

Assemble the signed body of one seal document.

default_project_keys(root[, refs])

Resolve a project's signing keys from its seal_keys member or refs.

diff_records(recorded, actual)

Diff two path-keyed record lists into discrepancies.

is_project_sealed(project_root)

Return whether one project carries a seal.

normalize_trusted_keys(trusted_keys)

Return trust anchors as canonical keys and fingerprints, skipping junk.

project_seal_path(project_root)

Return where one project's seal lives.

read_seal(path)

Read and structurally validate one seal document.

resolve_seal_keys(refs, *, project_root)

Resolve seal-key refs to the signing keys that are actually available.

seal_project(project_root, *[, keys])

Seal a project's loose files and every member's seal digest.

sign_seal_body(body, keys)

Digest a seal body and produce one detached signature per key.

unseal_project(project_root)

Remove a project's seal.

verify_project(project_root, *[, trusted_keys, deep])

Verify a project's seal and, when deep, every member seal it references.

verify_seal(path, *[, trusted_keys, expected_roles])

Verify one seal's body digest and signatures, and classify the signers.

write_seal(path, body, keys)

Sign a seal body and write the seal document atomically.

Package Contents

httk.core.project.PROJECT_DIRECTORY = 'httk_project'[source]
httk.core.project.PROJECT_FILE = 'project.json'[source]
httk.core.project.PUBLIC_KEY_PREFIX = 'ed25519:'[source]
exception httk.core.project.LegacyProjectError(message, *, root)[source]

Bases: ValueError

Raised when discovery finds an httk v1 ht.project directory.

Carries the offending directory as root so a caller that deliberately handles it (for example read-only verification of a v1 manifest) does not have to parse the message.

Parameters:
  • message (str) – Diagnostic explaining the legacy project and its remedy.

  • root (pathlib.Path) – Directory containing the legacy project marker.

root
httk.core.project.canonical_public_key(value)[source]

Normalize any accepted public key spelling to the recorded one.

Parameters:

value (str) – Accepted public-key text to normalize.

Returns:

Canonical prefixed public-key text.

Raises:

ValueError – If the public-key text is invalid.

Return type:

str

httk.core.project.discover_project(start=None)[source]

Find the nearest project root, or refuse a legacy one, at or above start.

Discovery walks from start and its parents, treating a file start as its containing directory. If it finds a legacy ht.project marker, the exception identifies the required remedy: run httk project import-v1 PATH.

Parameters:

start (str | os.PathLike[str] | None) – Directory or file from which to begin the upward search, or None for the current directory.

Returns:

Nearest project root, or None when no marker is found.

Raises:

httk.core.project.LegacyProjectError – If discovery finds a v1 project marker.

Return type:

pathlib.Path | None

httk.core.project.format_public_key(raw)[source]

Return the recorded representation of one raw Ed25519 public key.

Parameters:

raw (bytes) – Raw public-key bytes to record.

Returns:

Canonical prefixed public-key text.

Raises:

ValueError – If the key is not 32 bytes long.

Return type:

str

httk.core.project.import_v1_project(root, *, source=None, name=None)[source]

Create the project anchor from a legacy ht.project directory.

Only the anchor is created: the project’s metadata and the adoption of the legacy identities its old manifests were signed with. A workflow installation adds the workspace and any queue import on top of this.

Parameters:
  • root (str | os.PathLike[str]) – Directory in which to create the new project anchor.

  • source (str | os.PathLike[str] | None) – Legacy project directory, or root/ht.project when omitted.

  • name (str | None) – Optional replacement project name.

Returns:

Imported project metadata.

Raises:

FileNotFoundError – If the legacy project directory does not exist.

Return type:

dict[str, object]

httk.core.project.initialize_project(root, *, name, description='', manifest_exclusions=())[source]

Initialize the project anchor: its metadata, its key, and its remotes dir.

This creates only the anchor — httk_project with project.json, the project’s Ed25519 signing key, and the remotes directory. It creates no workflow workspace; a workflow installation layers that on top of the anchor so that a core-only installation still has a working project.

Parameters:
  • root (str | os.PathLike[str]) – Directory in which to create the project anchor.

  • name (str) – Human-readable project name.

  • description (str) – Optional project description.

  • manifest_exclusions (collections.abc.Iterable[str]) – Relative paths excluded from project manifests.

Returns:

Newly written project metadata.

Raises:

httk.core.project.LegacyProjectError – If root contains a v1 project marker.

Return type:

dict[str, object]

httk.core.project.key_fingerprint(value)[source]

Return the stable display fingerprint of one public key.

Parameters:

value (str) – Public-key text whose fingerprint is calculated.

Returns:

Stable SHA-256 fingerprint text.

Raises:

ValueError – If the public-key text is invalid.

Return type:

str

httk.core.project.parse_public_key(value)[source]

Decode a recorded public key, accepting the bare base64 spelling too.

Parameters:

value (str) – Public-key text to decode.

Returns:

Raw public-key bytes.

Raises:

ValueError – If the algorithm, encoding, or key length is invalid.

Return type:

bytes

httk.core.project.pin_project_key(root=None)[source]

Adopt the project’s current keys/project.pub as its trust anchor.

Pinning is always an explicit act. Verification trusts the key recorded in project.json and never the key a manifest carries in its own header, so adopting the key that is in the tree right now is exactly the decision an operator has to make consciously for an older project that has no pin.

Parameters:

root (str | os.PathLike[str] | None) – Project root, or None to discover the nearest project.

Returns:

Updated project metadata.

Raises:

ValueError – If no project exists or its public key is invalid.

Return type:

dict[str, object]

httk.core.project.pinned_project_key(metadata)[source]

Return the project’s own pinned public key, or None when absent.

Parameters:

metadata (collections.abc.Mapping[str, object]) – Project metadata containing the optional public-key member.

Returns:

Canonical pinned key, or None when the metadata has no key.

Raises:

ValueError – If the pinned key is present but invalid.

Return type:

str | None

httk.core.project.project_public_key_path(root)[source]

Return where a project keeps its own signing key’s public half.

Parameters:

root (str | os.PathLike[str]) – Project root containing the anchor.

Returns:

Path to the project’s public key file.

Return type:

pathlib.Path

httk.core.project.read_project(root)[source]

Read and validate the project.json of the project rooted at root.

Parameters:

root (str | os.PathLike[str]) – Project root whose manifest is read.

Returns:

Validated project metadata.

Raises:

ValueError – If the manifest is not an httk project manifest.

Return type:

dict[str, object]

httk.core.project.read_project_section(root, name)[source]

Return one named object member of project.json, empty when absent.

A section is a top-level member of the project manifest that some layer above the anchor owns — the workflow workspace registry, a campaign map — and reads and writes as a whole. The anchor does not interpret the member; it only guarantees that what a caller stores under a name comes back as the object it was, and refuses a member that some other writer has left as a non-object so a caller never silently reads a scalar as a mapping.

Parameters:
  • root (str | os.PathLike[str]) – Project root whose manifest is read.

  • name (str) – Top-level manifest member to retrieve.

Returns:

A copy of the named object, or an empty object when absent.

Raises:

ValueError – If the named manifest member is not an object.

Return type:

dict[str, object]

httk.core.project.read_public_key_file(path)[source]

Read one public-key file and return its recorded public key.

Parameters:

path (str | os.PathLike[str]) – Public-key file to read.

Returns:

Canonical public-key text from the first line.

Raises:

ValueError – If the file cannot be read, is empty, or contains an invalid key.

Return type:

str

httk.core.project.require_project(start=None)[source]

Return the nearest project root, refusing when there is none.

Parameters:

start (str | os.PathLike[str] | None) – Directory or file from which to begin the upward search, or None for the current directory.

Returns:

Nearest project root.

Raises:
Return type:

pathlib.Path

httk.core.project.trust_project_key(root, key)[source]

Adopt one further public key as a trust anchor of this project.

Parameters:
  • root (str | os.PathLike[str] | None) – Project root, or None to discover the nearest project.

  • key (str) – Public key to add to the project’s trusted anchors.

Returns:

Updated project metadata.

Raises:

ValueError – If no project exists, the key is invalid, or trusted_keys is invalid.

Return type:

dict[str, object]

httk.core.project.trusted_project_keys(metadata)[source]

Return every key metadata pins: the project’s own and any adopted one.

The pinned key of project.json is the trust anchor a manifest is checked against. trusted_keys carries the additional anchors an operator has adopted deliberately — most often the legacy identities an imported httk v1 project signed its old manifests with.

Parameters:

metadata (collections.abc.Mapping[str, object]) – Project metadata whose trust anchors are read.

Returns:

Unique canonical project and adopted trust anchors.

Raises:

ValueError – If trusted_keys is not a string array or contains an invalid key.

Return type:

tuple[str, Ellipsis]

httk.core.project.write_project_section(root, name, value)[source]

Store one named object member of project.json and return the metadata.

The write is an ordinary read-modify-write of the validated manifest, so the members the anchor owns are preserved untouched and only the named section is replaced. The section must be a mapping; the anchor stores its members verbatim without interpreting them.

Parameters:
Returns:

Updated project metadata.

Raises:

ValueError – If the named section is not a mapping.

Return type:

dict[str, object]

httk.core.project.PROJECT_PRIVATE_KEY_RELATIVE_PATH = 'httk_project/keys/project.seed'[source]

The exact private-key path created by the project identity implementation.

httk.core.project.export_project(out_path, project_path=None)[source]

Create a signed ZIP containing a project without private keys.

The path and suffix exclusions are primary. The content guard catches exact and re-encoded copies of known private material; transformed or truncated secrets are outside that guard’s scope.

Parameters:
  • out_path (str | pathlib.Path) – Destination ZIP path.

  • project_path (str | pathlib.Path | None) – Project root, or None to discover the nearest project.

Returns:

The destination path.

Raises:

ValueError – If the project is unsafe to export or its identity is invalid.

Return type:

pathlib.Path

httk.core.project.verify_export(zip_path, *, expect_key=None, trusted_keys=())[source]

Verify an exported project ZIP and return signer information.

Parameters:
Returns:

A JSON-ready verification report.

Raises:

ValueError – If the ZIP, manifest, files, tree digest, or signature is invalid.

Return type:

dict[str, object]

httk.core.project.DEFAULT_MANIFEST_EXCLUSIONS[source]
class httk.core.project.ManifestVerification[source]

What verifying one manifest against one tree established.

A signature check answers two separate questions. Does this manifest describe this tree, unaltered? is answered by the digests and the signature. Was it made by somebody this project trusts? is answered only by comparing the signing key with a trust anchor that did not come from the manifest.

Parameters:
  • verdict – Classify the verification result.

  • reason – Explain the classification.

  • manifest – Identify the verified manifest.

  • manifest_format – Identify the manifest format used.

  • public_key – Record the signing key, when readable.

  • trusted_keys – Record the trust anchors consulted.

verdict: str
reason: str
manifest: pathlib.Path
manifest_format: str
public_key: str | None = None
trusted_keys: tuple[str, Ellipsis] = ()
property valid: bool

Whether the manifest describes this tree and its signature verified.

property trusted: bool

Whether the verified signature was made by a pinned key.

property exit_code: int

The command-line status this verdict reports.

as_mapping()[source]

Return the JSON representation of this verdict.

Returns:

JSON-compatible verification members.

Return type:

dict[str, object]

httk.core.project.create_manifest(project=None, *, output=None)[source]

Create and atomically publish the signed v2 project manifest.

Parameters:
  • project (str | os.PathLike[str] | None) – Locate the project to snapshot, or use discovery when unset.

  • output (str | os.PathLike[str] | None) – Publish the manifest at this path, or use the project default.

Returns:

The published manifest path.

Raises:

ValueError – If the project is invalid or cannot be snapshotted.

Return type:

pathlib.Path

httk.core.project.project_exclusions(metadata)[source]

Return the default manifest exclusions plus the project’s configured ones.

Parameters:

metadata (dict[str, object]) – Project metadata carrying the optional exclusion member.

Returns:

The exclusion patterns applied before members contribute their own.

Raises:

ValueError – If manifest_exclusions is not an array of strings.

Return type:

tuple[str, Ellipsis]

httk.core.project.resolve_trusted_keys(project=None, *, trusted_keys=None)[source]

Return the trust anchors of project plus every explicitly named key.

An entry of trusted_keys is either a recorded key — ed25519:BASE64 or the bare base64 — or the path of a *.pub file holding one.

Parameters:
Returns:

Unique canonical trust-anchor values in stable order.

Raises:

ValueError – If an explicit key cannot be canonicalized.

Return type:

tuple[str, Ellipsis]

httk.core.project.verify_manifest(project=None, *, manifest=None, trusted_keys=None)[source]

Verify a project’s v2 manifest against the tree and its trust anchors.

The trust anchor is the key pinned in project.json — never the key the manifest names in its own header — plus any key passed in trusted_keys, as a recorded value or the path of a *.pub file.

Parameters:
Returns:

The detailed verification verdict.

Raises:
Return type:

ManifestVerification

class httk.core.project.ProjectMember[source]

One member a project holds: a subtree of a given kind.

Parameters:
  • path – The member’s posix relpath below the project root ("." is the project root itself).

  • kind – The member kind, whose handler owns the member’s internals.

  • name – The member’s recorded name, or None when it has none.

path: str
kind: str
name: str | None = None
class httk.core.project.ProjectMemberHandler[source]

Bases: Protocol

What core needs from the module that owns one member kind.

Every method takes the member’s own root — project_root / member.path — rather than the project, so a handler never has to rediscover where it lives. The manifest, repair, and verify verbs core owns call exactly these; a member seals through its own module, and core only records the resulting digest.

manifest_exclusions(project_root, member_relpath)[source]

Return the manifest exclusions this member contributes.

These are fnmatch patterns on posix relpaths below the project root (not the member root): the member decides which of its own internals — control directories, working scratch — the project manifest must leave out, while its payload files stay covered.

Parameters:
  • project_root (pathlib.Path) – The project root the patterns are relative to.

  • member_relpath (str) – This member’s relpath below the project root.

Returns:

The exclusion patterns this member contributes.

Return type:

tuple[str, Ellipsis]

seal_digest(member_root)[source]

Return this member’s identifier and the SHA-256 of its seal bytes.

The digest is what a project seal records for the member, so a project seal transitively pins the member without re-hashing its payload.

Parameters:

member_root (pathlib.Path) – This member’s root directory.

Returns:

The member identifier and the hex SHA-256 of its seal file.

Raises:

httk.core.project.sealing.SealError – If the member is unsealed.

Return type:

tuple[str, str]

verify(member_root, *, trusted_keys, deep)[source]

Verify this member and return its report entries.

Each entry is a mapping in the whole-tree verification shape — level, subject, valid, verdict, reason, signers, missing_signers, and discrepancies (a list of {kind, path}) — so the project report concatenates a member’s entries with its own.

Parameters:
  • member_root (pathlib.Path) – This member’s root directory.

  • trusted_keys (collections.abc.Sequence[str]) – Trust anchors to classify the signers against.

  • deep (bool) – Whether to recurse into every seal the member references.

Returns:

The member’s verification entries.

Return type:

tuple[dict[str, object], Ellipsis]

repair(member_root, *, apply)[source]

Repair this member, or report only, returning its findings.

Each finding is a mapping in the repair shape — check, status, message, repairable, repaired, action, details — so they concatenate with core’s own anchor findings. Repairs are applied by default; apply=False is a dry run that mutates nothing.

Parameters:
  • member_root (pathlib.Path) – This member’s root directory.

  • apply (bool) – Whether to apply repairs; False reports only.

Returns:

The member’s repair findings.

Return type:

tuple[dict[str, object], Ellipsis]

scan_project(project_root, *, apply, adopt)[source]

Optionally scan the whole project for members of this kind.

Core calls this once per registered kind at project scope, whether or not any member of the kind is registered, so a handler can surface members present on disk but missing from members.json — the exact rescue an empty registry needs. Repairs are applied by default; apply=False is a dry run that mutates nothing, and adopt tells the scan whether to also (re)establish members’ machine-local links on this machine. It is optional: core invokes it only when the handler defines it. Findings use the same mapping shape as repair().

Parameters:
  • project_root (pathlib.Path) – The project root to scan.

  • apply (bool) – Whether to apply repairs; False reports only.

  • adopt (bool) – Whether to adopt members of this kind on this machine.

Returns:

The project-scope findings for this kind.

Return type:

tuple[dict[str, object], Ellipsis]

adopt(member_root, *, name)[source]

Optionally (re)establish this member’s local links on this machine.

Adoption is the act of rebuilding whatever per-user or machine-local links a member needs to be usable here — for example, httk-workflow registers the member’s workspace in the per-user name registry under its recorded name. It is idempotent and never mutates sealed state. Core invokes it only when the handler defines it, passing the member’s recorded name; findings use the same mapping shape as repair().

Parameters:
  • member_root (pathlib.Path) – This member’s root directory.

  • name (str | None) – The member’s recorded name, or None when it has none.

Returns:

The adoption findings for this member.

Return type:

tuple[dict[str, object], Ellipsis]

guard(member_root)[source]

Return a context manager fencing the member while it is snapshotted.

A member that must be quiescent to be described faithfully — a workspace with running jobs — returns a guard that acquires that quiescence and raises if it cannot. A member with nothing to fence returns contextlib.nullcontext().

Parameters:

member_root (pathlib.Path) – This member’s root directory.

Returns:

A context manager held around the snapshot.

Return type:

contextlib.AbstractContextManager[object]

httk.core.project.members_path(project_root)[source]

Return where a project’s member registry lives.

Parameters:

project_root (str | os.PathLike[str]) – The project root whose member registry path to build.

Returns:

The httk_project/members.json path.

Return type:

pathlib.Path

httk.core.project.project_members(project_root)[source]

Return the members registered in a project, in recorded order.

A missing registry is an empty project, not an error.

Parameters:

project_root (str | os.PathLike[str]) – The project root whose members to read.

Returns:

The registered members.

Raises:

ValueError – If the registry file is malformed.

Return type:

tuple[ProjectMember, Ellipsis]

httk.core.project.register_project_member(project_root, path, kind, *, name=None)[source]

Record that a member of kind lives at path, idempotently.

The path is normalized to a posix relpath below the project root and a path outside the project is refused. Registering the same path again replaces its kind and name.

Parameters:
  • project_root (str | os.PathLike[str]) – The project root whose registry to update.

  • path (str | os.PathLike[str]) – The member’s path, absolute or relative to the project root.

  • kind (str) – The member kind whose handler owns the member.

  • name (str | None) – The member’s recorded name, or None for none.

Returns:

The members after the update.

Raises:
Return type:

tuple[ProjectMember, Ellipsis]

httk.core.project.set_project_member_name(project_root, path, name)[source]

Set (or clear) the recorded name of the member at path.

Parameters:
  • project_root (str | os.PathLike[str]) – The project root whose registry to update.

  • path (str | os.PathLike[str]) – The member’s path, absolute or relative to the project root.

  • name (str | None) – The name to record, or None to clear it.

Returns:

The members after the update.

Raises:
Return type:

tuple[ProjectMember, Ellipsis]

httk.core.project.unregister_project_member(project_root, path)[source]

Remove the member recorded at path.

Parameters:
  • project_root (str | os.PathLike[str]) – The project root whose registry to update.

  • path (str | os.PathLike[str]) – The member’s path, absolute or relative to the project root.

Returns:

The members after the removal.

Raises:
Return type:

tuple[ProjectMember, Ellipsis]

httk.core.project.update_project_member_path(project_root, old, new)[source]

Move the member recorded at old to new, keeping its kind.

Parameters:
  • project_root (str | os.PathLike[str]) – The project root whose registry to update.

  • old (str | os.PathLike[str]) – The member’s current path, absolute or relative to the root.

  • new (str | os.PathLike[str]) – The member’s new path, absolute or relative to the root.

Returns:

The members after the move.

Raises:
Return type:

tuple[ProjectMember, Ellipsis]

class httk.core.project.Discrepancy[source]

One way a sealed subject no longer matches its seal.

Parameters:
  • path – The record path or member relpath that disagrees.

  • kind – The disagreement: missing, extra, mismatch, unsealed (present but not recorded), or missing_job (recorded but absent).

path: str
kind: str
class httk.core.project.Seal[source]

One parsed seal document.

Parameters:
  • kind – The sealed level (project for a project seal).

  • subject – The identifiers of the sealed subject.

  • created_at – When the seal was written.

  • records – The recorded contents of the sealed subject.

  • body_sha256 – The recorded digest of the signed body.

  • signatures – The detached signatures over the body digest.

  • body_bytes – The canonical bytes the recorded digest is taken over.

  • path – Where the seal was read from.

kind: str
subject: dict[str, object]
created_at: str
records: tuple[dict[str, object], Ellipsis]
body_sha256: str
signatures: tuple[dict[str, object], Ellipsis]
body_bytes: bytes
path: pathlib.Path
exception httk.core.project.SealedError[source]

Bases: RuntimeError

An action was refused because the subject, or its enclosure, is sealed.

Changing a project’s members while the project is sealed is refused rather than silently allowed: a seal a project commits to must not change beneath it.

exception httk.core.project.SealError[source]

Bases: RuntimeError

A seal cannot be written or verified.

This is the cannot proceed failure: no signing key is available, or a member a project seal must cover is itself unsealed or has no handler. It never means an action was refused because something was already sealed; that is SealedError.

class httk.core.project.SealKeys[source]

The signing keys resolved for one seal, and the roles that were missing.

Parameters:
  • keys – The available signing keys, in the order their refs resolved.

  • missing_roles – The roles requested but which could not be resolved.

keys: tuple[SealKey, Ellipsis]
missing_roles: tuple[str, Ellipsis]
class httk.core.project.SealReport[source]

The verdicts of verifying a seal and, when deep, every seal below it.

Parameters:
  • entries – A flat sequence of report-entry mappings, parent before child, each in the shape SealVerification.as_entry() produces.

  • ok – Whether every entry is valid and at least one entry exists.

entries: tuple[dict[str, object], Ellipsis]
ok: bool
class httk.core.project.SealVerification[source]

What verifying one seal established.

Parameters:
  • valid – Whether the seal describes its subject and a signature verified.

  • verdict – One of VALID_TRUSTED, VALID_UNKNOWN_KEY, or INVALID.

  • reason – A human-readable explanation of the verdict.

  • signers – The fingerprints of the signatures that verified.

  • missing_signers – The expected roles that the seal did not carry.

  • discrepancies – How the subject diverges from the seal, if at all.

valid: bool
verdict: str
reason: str
signers: tuple[str, Ellipsis]
missing_signers: tuple[str, Ellipsis]
discrepancies: tuple[Discrepancy, Ellipsis]
as_entry(level, subject)[source]

Return this verdict as one whole-tree report entry.

Parameters:
  • level (str) – The subject level this verdict is for.

  • subject (str) – The subject identifier this verdict is for.

Returns:

The JSON-compatible report entry.

Return type:

dict[str, object]

httk.core.project.build_seal_body(kind, subject, records)[source]

Assemble the signed body of one seal document.

The returned mapping is the exact, canonical body a signature is taken over — its created_at is stamped now — so a caller signs and writes it with sign_seal_body() and write_seal().

Parameters:
Returns:

The unsigned seal body.

Return type:

dict[str, object]

httk.core.project.default_project_keys(root, refs=None)[source]

Resolve a project’s signing keys from its seal_keys member or refs.

Parameters:
Returns:

The resolved signing keys and the roles that could not be resolved.

Raises:

SealError – If the member is malformed or no key resolves.

Return type:

SealKeys

httk.core.project.diff_records(recorded, actual)[source]

Diff two path-keyed record lists into discrepancies.

Parameters:
Returns:

The per-path discrepancies, sorted by path.

Return type:

list[Discrepancy]

httk.core.project.is_project_sealed(project_root)[source]

Return whether one project carries a seal.

Parameters:

project_root (str | os.PathLike[str]) – The project root to check.

Returns:

Whether the project seal file exists.

Return type:

bool

httk.core.project.normalize_trusted_keys(trusted_keys)[source]

Return trust anchors as canonical keys and fingerprints, skipping junk.

Parameters:

trusted_keys (collections.abc.Iterable[str]) – Trust anchors as ed25519: keys or sha256: fingerprints.

Returns:

The canonical keys and fingerprints, with unparseable entries dropped.

Return type:

set[str]

httk.core.project.project_seal_path(project_root)[source]

Return where one project’s seal lives.

Parameters:

project_root (str | os.PathLike[str]) – The project root whose seal path to build.

Returns:

The project seal path.

Return type:

pathlib.Path

httk.core.project.read_seal(path)[source]

Read and structurally validate one seal document.

Parameters:

path (str | os.PathLike[str]) – The seal file to read.

Returns:

The parsed seal.

Raises:
  • ValueError – If the file is not a valid seal document.

  • OSError – If the file cannot be read.

Return type:

Seal

httk.core.project.resolve_seal_keys(refs, *, project_root)[source]

Resolve seal-key refs to the signing keys that are actually available.

A ref is project (the project’s own signing seed, discovered from project_root), identity (the default operator identity), identity:<short> (a named identity), or the path of a base64 Ed25519 seed file. An unavailable ref is skipped and logged rather than fatal; only resolving no key at all is an error.

Parameters:
Returns:

The resolved keys and the roles that could not be resolved.

Raises:

SealError – If no key at all could be resolved.

Return type:

SealKeys

httk.core.project.seal_project(project_root, *, keys=None)[source]

Seal a project’s loose files and every member’s seal digest.

Every registered member must already be sealed, and every member kind must have a registered handler; otherwise the project cannot be sealed.

Parameters:
  • project_root (str | os.PathLike[str]) – The project root to seal.

  • keys (SealKeys | None) – The signing keys, or None to use the project default.

Returns:

The project seal path.

Raises:

SealError – If a member is unsealed, a kind has no handler, or no key resolves.

Return type:

pathlib.Path

httk.core.project.sign_seal_body(body, keys)[source]

Digest a seal body and produce one detached signature per key.

Parameters:
Returns:

The hex body digest and the detached signatures.

Return type:

tuple[str, list[dict[str, object]]]

httk.core.project.unseal_project(project_root)[source]

Remove a project’s seal.

Parameters:

project_root (str | os.PathLike[str]) – The project root to unseal.

httk.core.project.verify_project(project_root, *, trusted_keys=(), deep=True)[source]

Verify a project’s seal and, when deep, every member seal it references.

Parameters:
  • project_root (str | os.PathLike[str]) – The project root to verify.

  • trusted_keys (collections.abc.Iterable[str]) – Trust anchors to classify the signers against.

  • deep (bool) – Whether to delegate to each member handler’s own verification.

Returns:

The flat report of every verdict, with an overall ok.

Return type:

SealReport

httk.core.project.verify_seal(path, *, trusted_keys=(), expected_roles=())[source]

Verify one seal’s body digest and signatures, and classify the signers.

This checks the seal against itself, not against the subject: at least one signature must verify, any signature that does not makes the seal invalid, and the seal is trusted when a verifying signer is a pinned key or fingerprint. Subject-level checks are done by verify_project().

Parameters:
Returns:

The signature verdict.

Raises:
  • ValueError – If the file is not a valid seal document.

  • OSError – If the file cannot be read.

Return type:

SealVerification

httk.core.project.write_seal(path, body, keys)[source]

Sign a seal body and write the seal document atomically.

Parameters:
Returns:

The written seal path.

Raises:

SealError – If no signing key is available.

Return type:

pathlib.Path