Skip to main content

Reference validator for the OpenAKB descriptor format (spec major v1).

Project description

openakb-validate

openakb-validate is the pure Python reference validator library for the OpenAKB descriptor format, spec major v1. It is a library only: no CLI, no network access, no background services, and no storage, search, freshness, authentication, or transport mechanism.

The package validates caller-provided descriptor data and reports stable findings. It does not fetch referenced resources unless the caller supplies an explicit resolver. Source, issues, and the normative specification live in the openakb/spec repository.

Install

Install from the package index into Python 3.12 or newer:

python -m pip install openakb-validate

For repository development:

cd packages/python
uv sync

Validate a descriptor

Use validate for descriptor-only validation. The input is already-parsed JSON data; callers own file reading and output formatting.

import json
from pathlib import Path

from openakb_validate import ValidationResult, validate

descriptor = json.loads(Path("openakb.json").read_text(encoding="utf-8"))
result: ValidationResult = validate(descriptor)

if not result.ok:
    for finding in result.findings:
        print(f"{finding.code}: {finding.message}")

Each Finding has a stable code, RFC 6901 JSON Pointer path, and message. Its .name property derives the public finding name from the code catalog and, for a code outside the catalog, echoes the code back rather than raising. Advisory diagnostics use Advisory and appear in warnings; they never affect the validation verdict. The re-exported json_pointer helper builds the same canonical pointers the validator emits, for callers that construct or match against finding.path.

The stable validation codes for spec major v1 are AKB001 through AKB012, defined in spec §7, Validation and error codes. Those code meanings are part of the public contract for v1 and only change through the spec process.

Extract citations

Use extract_citations to read normative [cite:] markers from Markdown prose. The parser follows CommonMark structure, so markers in code and HTML constructs remain literal text. Each returned Citation contains source ids in written order, including duplicates.

from openakb_validate import Citation, extract_citations

citations: list[Citation] = extract_citations("See [cite: source_a, source_b].")
ids = [citation.ids for citation in citations]

Strict mode

Strict mode adds AKB006 unknown-core-property findings for unknown core members outside extension payloads:

import json
from pathlib import Path

from openakb_validate import validate

descriptor = json.loads(Path("openakb.json").read_text(encoding="utf-8"))
result = validate(descriptor, strict=True)
unknown_core_findings = [
    finding for finding in result.findings if finding.code == "AKB006"
]

Strict mode remains local and deterministic. It does not make network requests or freshness decisions. Advisory warnings describe MAY-warn surfaces and never affect result.ok.

Check content

Content checks are opt-in: check_content only fetches a descriptor's referenced resources through a resolver you supply, then verifies hashes, provenance sidecars, citation markers, and claim quotes. LocalFileResolver(base_dir=...) reads files under a caller-controlled directory.

import json
from pathlib import Path

from openakb_validate import ContentReport, LocalFileResolver, check_content

descriptor = json.loads(Path("openakb.json").read_text(encoding="utf-8"))
resolver = LocalFileResolver(base_dir="example-akb")
report: ContentReport = check_content(descriptor, resolver=resolver)

if report.failed:
    for check in report.failed:
        print(f"FAILED {check.kind} {check.path}: {check.detail}")
elif not report.verified:
    print("nothing verified: no content was fetched or there was nothing to check")
else:
    print(f"verified {len(report.verified)} content reference(s)")

for finding in report.findings:
    print(f"{finding.code}: {finding.message}")

Three outcomes, and why ok can be silent

Every content check reports one of three outcomes, exposed as the module constants VERIFIED, FAILED, and UNVERIFIABLE:

  • verified — the resource was fetched and matched: a hash matched, a quote was found in its capture, or citation markers resolved.
  • failed — the resource was fetched and did not match: a hash mismatch, an absent quote, or malformed fetched content. Tampering surfaces here.
  • unverifiable — the resource could not be fetched, or there was nothing to check it against. Nothing was proven either way; this is never an error.

report.ok is True when no check failed and no structural finding was raised. It is deliberately not "everything verified": a report in which every check is unverifiable — because nothing was fetched — is still ok. To tell "all good" from "nothing checked", read the outcome accessors, which mirror report.failed:

  • report.verified, report.failed, report.unverifiable collect the checks with each outcome.
  • report.findings carries only the structural findings raised inside content (for example, a citation marker that points at a missing source). A hash or quote mismatch is not a structural finding — it appears in report.failed. Consuming code that watches only report.findings will therefore miss a corrupted capture; key on report.failed (and report.ok) to catch tampering.

Filter checks by their kind using the exported constants rather than hard-coding strings: KIND_GUIDE_HASH, KIND_CONTENT_HASH, KIND_CITATIONS, KIND_SIDECAR, KIND_CAPTURE, and KIND_QUOTE.

import json
from pathlib import Path

from openakb_validate import KIND_CAPTURE, LocalFileResolver, check_content

descriptor = json.loads(Path("openakb.json").read_text(encoding="utf-8"))
report = check_content(descriptor, resolver=LocalFileResolver(base_dir="example-akb"))

capture_checks = [check for check in report.checks if check.kind == KIND_CAPTURE]

Resolvers

LocalFileResolver accepts a str or os.PathLike[str] base_dir and confines reads to files beneath it, rejecting absolute paths, .. traversal, queries, and path parameters before any read. That path policing is specific to LocalFileResolver.

Implement the Resolver protocol when content lives somewhere other than a local directory:

from openakb_validate import Resolver, Unfetchable


class MappingResolver(Resolver):
    """Resolve references from an in-memory mapping."""

    def __init__(self, files: dict[str, bytes]) -> None:
        self._files = files

    def fetch(self, uri: str) -> bytes:
        try:
            return self._files[uri]
        except KeyError as error:
            raise Unfetchable(uri) from error

check_content calls fetch(uri) with the effective reference: the descriptor reference (a content_uri, capture_uri, guide_uri, or provenance_uri) already joined onto the descriptor's base_uri when one is present, with any #fragment stripped. A resolver returns the resource bytes or raises Unfetchable; anything it cannot fetch becomes an unverifiable outcome, never an error. A custom resolver receives none of LocalFileResolver's path guards and owns its own safety.

Validate everything in one call

Use validate_with_content when a caller wants descriptor validation, strict-mode advisories, provenance checks, and content checks through one API.

import json
from pathlib import Path

from openakb_validate import FullReport, LocalFileResolver, validate_with_content

descriptor = json.loads(Path("openakb.json").read_text(encoding="utf-8"))
report: FullReport = validate_with_content(
    descriptor,
    resolver=LocalFileResolver(base_dir="example-akb"),
    strict=True,
)

if not report.ok:
    for finding in report.validation.findings:
        print(f"{finding.code}: {finding.message}")
    for check in report.content.failed:
        print(f"FAILED {check.kind} {check.path}: {check.detail}")

report.ok combines structural validity and content checks (validation.ok and content.ok), so it carries the same caveat: it does not require that anything was verified. This is still a pure library call — callers choose where data comes from, how findings are displayed, and whether any finding blocks their workflow.

Conformance

The package participates in the shared OpenAKB conformance suite for spec major v1. Conformance fixtures are the cross-language contract for validation behavior, including the stable AKB001 through AKB012 code mapping.

Package development emits and checks a conformance report with:

cd packages/python
uv run python tests/conformance_report.py > conformance-report.json
node ../../scripts/ci/check-conformance-report.mjs conformance-report.json

These commands are the package conformance/report gate used by CI and release checks.

Development and contributing

Use uv for package work:

cd packages/python
uv sync
uv run pytest
uv run ruff format --check .
uv run ruff check .
uv run mypy

The package vendors byte-identical copies of the published schemas from schema/v1/ so they are available through importlib.resources. Check drift from the repository root:

bash scripts/ci/check-schema-sync.sh

Follow the package conventions in AGENTS.md and the repository conventions in the root AGENTS.md, and see CONTRIBUTING.md for the contribution process. Public artifacts must remain vendor-neutral.

License

Apache-2.0.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

openakb_validate-0.1.0.tar.gz (46.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

openakb_validate-0.1.0-py3-none-any.whl (33.5 kB view details)

Uploaded Python 3

File details

Details for the file openakb_validate-0.1.0.tar.gz.

File metadata

  • Download URL: openakb_validate-0.1.0.tar.gz
  • Upload date:
  • Size: 46.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for openakb_validate-0.1.0.tar.gz
Algorithm Hash digest
SHA256 9530e10fb1c5058517b683f6e11f2b6889b930706a2d7c7a1bbc760a277bb1f7
MD5 2607674e4ac7e69efff52bc34a036ccc
BLAKE2b-256 bd1c1b8c00adfe12b7c325b945ee45bcf32ed06ffec25f446296c8792428e372

See more details on using hashes here.

Provenance

The following attestation bundles were made for openakb_validate-0.1.0.tar.gz:

Publisher: publish-python.yml on openakb/spec

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file openakb_validate-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for openakb_validate-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 83202714404300f8243663ad9fa1a28d9e891de52a771771a223402923053775
MD5 6141ffc784a7380c56a67af09848dd34
BLAKE2b-256 123643d26025bb2b85758710725c866523d156ba05089e94a2bfc06144acfed7

See more details on using hashes here.

Provenance

The following attestation bundles were made for openakb_validate-0.1.0-py3-none-any.whl:

Publisher: publish-python.yml on openakb/spec

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page