Skip to main content

Auditable, version-pinned provenance for AI-produced values — zero dependencies.

Project description

attestable

Make it structurally impossible to ship an AI-produced value without a confidence score and a version-pinned, resolvable citation.

attestable is a tiny, zero-dependency Python library for auditable provenance on model output. It gives you four things that compose:

  • Derived[T] — a value that cannot be constructed without a confidence in [0, 1] and at least one valid source anchor. Provenance travels with the value, by construction, everywhere it goes.
  • An anchor grammar — a frozen citation URI that pins the source version, so re-chunking or re-embedding a newer revision never invalidates a citation stored against an older one. This is the one correctness property hand-rolled citation schemes get wrong.
  • Resolvers + dangling detection — resolve anchors back to source text, and batch-check a corpus for citations that no longer point anywhere.
  • Attestations — deterministic, tamper-evident (optionally HMAC-signed) digests that prove a value/confidence/citation triple has not been altered.

CI PyPI Python License


Why

Auditors, regulators, and reviewers reject "the model said so." When an LLM extracts a field, answers a question, or flags a finding, three things must be true for that output to survive scrutiny:

  1. It carries a confidence you can threshold on.
  2. It carries a pointer to the exact source it came from.
  3. That pointer still works after you re-index the corpus.

Most stacks bolt this on as an afterthought and get (3) wrong: citations are stored as raw offsets or chunk ids that break the moment a document is re-chunked. attestable makes (1) and (2) a type-level requirement and makes (3) correct by pinning the source version in the citation itself.

It is deliberately small, dependency-free, offline-capable, and framework-agnostic — it composes with your extraction/RAG stack rather than replacing it.

Install

pip install attestable.py

The PyPI distribution is named attestable.py (the plain attestable name is an unrelated, unmaintained package). The import name is unaffected: import attestable.

Requires Python 3.10+. No third-party runtime dependencies.

Quick start

from attestable import Derived, span_anchor, attest, verify, InMemoryResolver, parse_anchor

# 1. Cite the exact characters — pinned to version 3 of the source.
anchor = span_anchor("document", "invoice-abc", version=3, chunk_id="p2", start=40, end=52)

# 2. Wrap the value. This raises unless confidence is in [0,1] and provenance is a valid anchor.
total = Derived(value="2026-01-15", confidence=0.94, provenance=[anchor])

# 3. Attest it (keyed = forge-resistant).
token = attest(total, key="rotate-me")
assert verify(total, token, key="rotate-me")

# 4. Tampering is detected.
assert not verify(total.with_value("2026-02-02"), token, key="rotate-me")

# 5. Resolve back to source, and detect citations that no longer point anywhere.
resolver = InMemoryResolver()
resolver.put("document", "invoice-abc", 3, "… issued on 2026-01-15 …")
assert resolver.resolve(parse_anchor(anchor)) is not None

Run the full tour: python examples/quickstart.py.

The anchor grammar

attest://<type>/<lineage_id>@<version>
attest://<type>/<lineage_id>@<version>#chunk:<chunk_id>
attest://<type>/<lineage_id>@<version>#span:<chunk_id>:<start>-<end>
attest://<type>/<lineage_id>@<version>#field:<json_pointer>
Part Meaning
type lowercase entity type, e.g. document or document.sop
lineage_id identity that is stable across versions of the source
version the specific integer version cited
chunk_id opaque, stable id of a chunk/paragraph
start/end character range within a chunk (start <= end)
json_pointer RFC-6901 pointer into a structured record

parse_anchor() is total: it returns a fully-typed ParsedAnchor or raises AnchorSyntaxError — never a half-populated result. The scheme (attest) is fixed per deployment; use Grammar("your-scheme") to change it.

Integrating with your store

Implement the Resolver protocol over wherever your source text actually lives:

from attestable import Resolver, ResolvedSource, ParsedAnchor

class SqliteResolver:
    def resolve(self, anchor: ParsedAnchor) -> ResolvedSource | None:
        row = db.get(anchor.entity_type, anchor.lineage_id, anchor.version)
        if row is None:
            return None                       # -> reported by find_dangling()
        text = row.text[anchor.start:anchor.end] if anchor.fragment == "span" else row.text
        return ResolvedSource(anchor=anchor.to_anchor(), text=text)

Then find_dangling(anchors, resolver) and dangling_in(derived_values, resolver) give you a corpus-wide integrity check to run after every re-ingest.

Documentation

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

attestable_py-0.1.1.tar.gz (19.2 kB view details)

Uploaded Source

Built Distribution

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

attestable_py-0.1.1-py3-none-any.whl (18.4 kB view details)

Uploaded Python 3

File details

Details for the file attestable_py-0.1.1.tar.gz.

File metadata

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

File hashes

Hashes for attestable_py-0.1.1.tar.gz
Algorithm Hash digest
SHA256 5bc539dc648cd6add713ca4b16354265200b8ce1f85ed83b70152bbc181c23a0
MD5 a853d87724a3dd5e8ca38fec83c78f1d
BLAKE2b-256 370d2cc2cde16dff52bf3bb4bc8cb949b5124114671a97cfe5d5e5e997812d59

See more details on using hashes here.

Provenance

The following attestation bundles were made for attestable_py-0.1.1.tar.gz:

Publisher: release.yml on Vibecoder012/attestable

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

File details

Details for the file attestable_py-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: attestable_py-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 18.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for attestable_py-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 0bf375dbe8ac9240c3e5c965243d5cfd554d34624d0096e0b6daa5419e10cf63
MD5 c6448df9eaaf6938d343ecd4e18a922b
BLAKE2b-256 27630f59eb20c3088820c87275af5078a2b3ef39a9deff793e6f9363fb6689e5

See more details on using hashes here.

Provenance

The following attestation bundles were made for attestable_py-0.1.1-py3-none-any.whl:

Publisher: release.yml on Vibecoder012/attestable

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