Skip to main content

IntentProof: verifiable execution records—intent, action, and proof for every wrapped call.

Project description

Logs narrate; IntentProof gives you proof.

IntentProof is auditable execution records for actions that must be defensible—intent tied to what actually ran.

Wrap the calls that matter; each invocation emits one verifiable ExecutionEvent, structured so intent and outcome can be reconciled with reality—not only observed.

Observability captures what happened. IntentProof tells you whether it matched what was meant to happen.

Every ExecutionEvent contains:

  • intent: what this invocation was meant to prove
  • action: the stable operation id for this step
  • status: success or error
  • inputs and output: what the runtime saw going in and coming out

Why this matters

Modern systems—especially AI agents—do not only compute; they act: issuing refunds, sending emails, updating databases.

When something goes wrong, logs tell you what ran. They don't tell you:

  • what was supposed to happen
  • whether all steps completed
  • whether systems ended up in a consistent state

IntentProof exists to bridge that gap.

It records intent alongside execution so systems can be verified, not just observed.

Picture this:

It's 4:47 on a Friday. A customer insists the critical action never happened. Support sees scattered traces; engineering sees green checks; finance asks for one clean chain: what was supposed to occur, what did occur, and whether the outcome is complete.

Ordinary telemetry shows that something ran. It rarely ships an auditable story you can hand to someone who doesn't read your codebase. IntentProof exists for when the question stops being "what was logged?" and starts being "prove it."

Requirements

  • Python 3.11 or newer

Install

Package: intentproof-sdk (PyPI).

Pin the version you want from PyPI or from GitHub Releases. Replace x.y.z below with that version.

pip install intentproof-sdk==x.y.z

Quick start

from intentproof import client

refund = client.wrap(
    intent="Initiate refund",
    action="stripe.refunds.create",
    fn=lambda inp: stripe_refunds_create(inp),
)

Each refund call emits one ExecutionEvent with the intent and action you chose, the inputs and output (or error + status: "error"), and timing fields—an execution record you can inspect, export, or verify later.

Reference

Detailed tables for the client API, emitted events, configuration, and related exports.

IntentProofClient API

Member Description
__init__(config=None) Creates a client. Default exporters: a single MemoryExporter if you omit config.exporters.
configure(config) Re-applies IntentProofConfig fields (exporters, error hook, defaults, stack policy).
wrap(...) Returns a callable that records one ExecutionEvent per invocation (sync or async). Options must satisfy assert_wrap_options_shape (intent / action non-empty strings, etc.).
flush() Awaits flush() on every Exporter that implements it, in parallel.
shutdown() For each Exporter, awaits shutdown() if implemented, otherwise flush() if implemented.
get_correlation_id() Returns the correlation ID from contextvars (or equivalent), if any.
with_correlation(fn) Runs fn with a fresh UUID as correlation ID for nested wraps.
with_correlation(id, fn) Runs fn with id stripped; blank / whitespace-only id falls back to a UUID.

Module-level helpers (same package as the client)

These use the same correlation context as IntentProofClient instances:

Export Description
create_intent_proof_client(config=None) New isolated client (tests, workers, multi-tenant).
get_intent_proof_client() Lazy singleton used by client.
client Default singleton instance.
get_correlation_id() Same behavior as the instance method.
run_with_correlation_id(id, fn) Requires a non-empty correlation ID after strip; raises if invalid.
assert_correlation_id(id) Runtime assertion for correlation ID shape.
assert_wrap_options_shape(options) Runtime validation for WrapOptions.

ExecutionEvent fields

Field Description
id Unique event id (UUID).
correlationId Request or trace correlation ID when present—usually from context or WrapOptions.
intent Human-readable label for what this invocation is meant to prove (outcome, policy goal, or domain).
action Stable operation id for this step (often dotted or namespaced).
inputs JSON-safe snapshot of call arguments (default) or capture_input result.
output JSON-safe return value or capture_output result on success. When status is "error", set only if capture_error returned a value.
error On failure: name, message, and optional stack (see include_error_stack).
status "ok" if the wrapped call completed normally; "error" if it raised.
startedAt Start time (ISO 8601).
completedAt Completion time (ISO 8601).
durationMs Wall time between start and completion, in milliseconds.
attributes Optional plain mapping (string / number / boolean values only), merged from client defaults and wrap options.

WrapOptions and IntentProofConfig

WrapOptions (passed to wrap)

Field Description
intent, action Required, non-empty after strip.
correlationId Optional; when set, non-empty after strip. Otherwise the active correlation ID from context is used, if any.
attributes Per-invocation dimensions merged over default_attributes.
capture_input, capture_output, capture_error Optional hooks to replace default snapshot behavior for inputs, success output, or error-side extra output.
include_error_stack When False, omit error.stack for this wrap (overrides client default).
max_depth, max_keys, redact_keys, max_string_length Forwarded to snapshot for inputs and outputs (see SerializeOptions in type hints).

IntentProofConfig (__init__ / configure)

Field Description
exporters Ordered list of Exporter instances; each receives every ExecutionEvent.
on_exporter_error Called when any exporter’s export() raises or returns a failed future. Defaults to logging / stderr.
default_attributes Merged into every event’s attributes (wrap-specific attributes win on key collision).
include_error_stack Default True; set False in production if stacks must not leave the trust zone.

Related exports

  • MemoryExporter, HttpExporter, BoundedQueueExporter — Delivery implementations; each implements Exporter.
  • snapshot — Same JSON-safe serializer the client uses internally, if you build custom tooling.
  • VERSION — Package version string (e.g. from importlib metadata at runtime).

Examples

1 — Refund and customer receipt

Support approves order ORD-1042. Your service creates the Stripe refund, then emails the customer a receipt. run_with_correlation_id ties both calls to req_refund_ord_1042. Each wrap defines its own intent (the outcome you are proving for that step) and action (how it is done); correlationId is what stitches them together.

capture_input / capture_output trim each record to the fields you want in proof (refund id, amounts, message id)—not full vendor payloads.

JSON on the wire uses camelCase; Python wrap options use snake_case (e.g. capture_input).

from intentproof import client, run_with_correlation_id

create_refund = client.wrap(
    intent="Return captured funds to the customer's original card network",
    action="stripe.refund.create",
    attributes={"vendor": "stripe", "step": "refund_money"},
    capture_input=lambda args: {
        "paymentIntentId": args[0]["paymentIntentId"],
        "amountCents": args[0]["amountCents"],
        "reason": args[0].get("reason"),
    },
    capture_output=lambda result: {
        "refundId": result["id"],
        "status": result["status"],
        "amountCents": result["amountCents"],
    },
    fn=lambda inp: {
        "id": "re_3SAMPLEabcdefghijklmnop",
        "status": "succeeded",
        "amountCents": inp["amountCents"],
    },
)

send_refund_receipt = client.wrap(
    intent="Deliver a customer-visible refund confirmation for the ledger entry",
    action="email.customer.refund_receipt",
    attributes={"channel": "email", "step": "notify_customer"},
    capture_input=lambda args: {
        "customerId": args[0]["customerId"],
        "orderId": args[0]["orderId"],
        "refundId": args[0]["refundId"],
        "amountCents": args[0]["amountCents"],
    },
    capture_output=lambda result: {
        "messageId": result["messageId"],
        "status": result["status"],
    },
    fn=lambda p: {"messageId": "msg_49401_sample", "status": "queued"},
)


def refund_flow():
    with run_with_correlation_id("req_refund_ord_1042"):
        refund = create_refund(
            {
                "paymentIntentId": "pi_3SAMPLEabcdefghijklmnop",
                "amountCents": 4999,
                "reason": "requested_by_customer",
            }
        )
        send_refund_receipt(
            {
                "customerId": "cus_SAMPLEabcdefghijkl",
                "orderId": "ORD-1042",
                "refundId": refund["id"],
                "amountCents": refund["amountCents"],
            }
        )

Emitted ExecutionEvent values (same correlationId on each; distinct intent per step; id / timestamps omitted):

[
  {
    "correlationId": "req_refund_ord_1042",
    "intent": "Return captured funds to the customer's original card network",
    "action": "stripe.refund.create",
    "inputs": {
      "paymentIntentId": "pi_3SAMPLEabcdefghijklmnop",
      "amountCents": 4999,
      "reason": "requested_by_customer"
    },
    "status": "ok",
    "output": {
      "refundId": "re_3SAMPLEabcdefghijklmnop",
      "status": "succeeded",
      "amountCents": 4999
    },
    "attributes": {
      "service": "billing-api",
      "env": "test",
      "vendor": "stripe",
      "step": "refund_money"
    }
  },
  {
    "correlationId": "req_refund_ord_1042",
    "intent": "Deliver a customer-visible refund confirmation for the ledger entry",
    "action": "email.customer.refund_receipt",
    "inputs": {
      "customerId": "cus_SAMPLEabcdefghijkl",
      "orderId": "ORD-1042",
      "refundId": "re_3SAMPLEabcdefghijklmnop",
      "amountCents": 4999
    },
    "status": "ok",
    "output": { "messageId": "msg_49401_sample", "status": "queued" },
    "attributes": {
      "service": "billing-api",
      "env": "test",
      "channel": "email",
      "step": "notify_customer"
    }
  }
]

2 — Payment failure with operator metadata (capture_error)

When a capture raises, the record still carries status: "error" and error.message for proof of failure. capture_error adds a small, JSON-safe output for dashboards (e.g. decline code) without pretending the business call succeeded.

def decline_card(_input):
    raise RuntimeError("Your card was declined.")


capture_payment = client.wrap(
    intent="Capture authorized funds",
    action="stripe.payment_intent.capture",
    capture_input=lambda args: {"paymentIntentId": args[0]["paymentIntentId"]},
    capture_error=lambda _err: {"code": "card_declined", "retryable": False},
    fn=decline_card,
)

try:
    capture_payment({"paymentIntentId": "pi_3SAMPLEabcdefghijklmnop"})
except RuntimeError:
    pass  # card declined — expected
{
  "intent": "Capture authorized funds",
  "action": "stripe.payment_intent.capture",
  "inputs": { "paymentIntentId": "pi_3SAMPLEabcdefghijklmnop" },
  "status": "error",
  "error": {
    "name": "RuntimeError",
    "message": "Your card was declined."
  },
  "output": { "code": "card_declined", "retryable": false }
}

3 — Proof delivery over HTTP (same ExecutionEvent shape)

HttpExporter POSTs the same ExecutionEvent your verifiers see in memory—here alongside MemoryExporter so tests can assert the wire without a real collector. The request omits ambient credentials; the body is { "intentproof": "1", "event": … } (see exporter implementation). For authenticated collectors, pass headers (e.g. Authorization, API keys) — see Security.

run_probe = client.wrap(intent="HTTP test", action="test.http", fn=lambda: 42)
run_probe()
{
  "intent": "HTTP test",
  "action": "test.http",
  "inputs": [],
  "status": "ok",
  "output": 42
}

Security

For vulnerability reporting, use this repository’s Security tab (private advisories).

Every ExecutionEvent you emit is data you may ship off-process. Treat them like audit-grade execution records: they can include PII, secrets, stack traces, and business identifiers depending on your snapshot / capture_* hooks.

  • Minimize payload: Use redact_keys, max_depth / max_keys / max_string_length, and narrow capture_input / capture_output / capture_error so proof records contain only what verifiers need.
  • Stacks: Set include_error_stack: False on the client (or per wrap) when traces must not leave your trust zone.
  • HTTP ingest: Keep collector url and any redirect behavior under trusted configuration (avoid SSRF if URLs were ever influenced by untrusted input). Prefer HTTPS and short-lived credentials end-to-end.
  • HttpExporter auth: Pass credentials in headers (for example Authorization: Bearer …, x-api-key, or whatever your collector expects). The SDK does not log header values; use short-lived tokens and scope them to ingest only.
  • Runtime surface: This package targets CPython; treat the ingest endpoint and headers with the same care you would for any outbound credential (including sandboxed or embedded runtimes).
  • Delivery semantics: Exporter failures invoke on_exporter_error and do not roll back the wrapped callable’s side effects—design compensating controls if you need strict “delivered exactly once” guarantees.

Custom body serializers: if body(event) raises, HttpExporter notifies on_error and falls back to the same JSON envelope path as the default serializer (full event, then a partial envelope, then a minimal eventSerializeFailed payload) so export() still completes and the configured HTTP client runs when possible.


Canonical specification (intentproof-spec)

Shared pins and terminology (INTENTPROOF_SPEC_ROOT, spec-commit, script names): intentproof-spec CONTRIBUTING — Terminology.

Schemas, golden oracles, and the Vitest conformance oracle live in the IntentProof specification repository (intentproof-spec).

  • Version pin: [tool.intentproof].spec-version and spec-commit in pyproject.toml match spec.json and the spec HEAD checkout; scripts/check-sdk-spec-pin.sh enforces this before conformance.

  • CI: every push/PR checks out this SDK plus intentproof-spec and runs scripts/spec-conformance.sh (pin check + full oracle; see .github/workflows/ci.yml). The spec-golden-parity job runs tests/unit/test_spec_golden_conformance.py against the same golden/execution_event_cases.jsonl using jsonschema + semantics mirrored from the spec (tests/spec_semantics.py).

  • Local: clone intentproof-spec next to this repository (../intentproof-spec), then:

    tox -e spec
    

    Or set INTENTPROOF_SPEC_ROOT and run bash scripts/spec-conformance.sh.

  • Generated fingerprint metadata: model generation writes src/intentproof/generated/spec_fingerprint.json (spec version, generator version, per-schema SHA-256, aggregate hash). Validate/update generated artifacts with:

    bash scripts/verify-generated-types.sh
    
  • No handwritten model types: scripts/check-no-handwritten-model-types.sh delegates to the shared intentproof-spec checker. It runs in CI (dedicated no-handwritten-model-types job—SDK + spec checkout and python3 only—and tox -e static), release publish, and release preflight required checks, and fails if schema model class definitions appear outside src/intentproof/generated or if the bridge aliases in src/intentproof/types.py stop mapping to generated models.


Project development

Contributing and shared intentproof-spec terminology: CONTRIBUTING.md.

Layout: src/ tree, built with Hatchling (Hatch; build-backend = "hatchling.build" in pyproject.toml). Requires Python 3.11 or newer. Release history: CHANGELOG.md.

Checks run via tox (tox.ini): static runs ruff (format + lint); cov runs pytest with pytest-cov and enforces 100% line coverage; py311py314 install the package with dev extras and run pytest. CI matches this.

pip install "tox>=4"          # or: pipx install tox
tox run -e static             # ruff only (matches CI static job)
tox run -e cov                # pytest + 100% coverage gate (matches CI cov job)
tox run -e audit              # CVE scan (pip-audit; dev/build toolchain)
tox run -e ALL                # static + every Python on PATH (missing interpreters skipped)
python -m build               # optional wheel/sdist — uses dev extra: pip install -e ".[dev]"

Supply chain: Runtime dependencies are empty; pip-audit checks dev tooling (and future runtime deps). Run pip-audit after pip install -e ".[dev]", or tox run -e audit. On GitHub, Dependabot (.github/dependabot.yml) proposes weekly updates for pyproject.toml and GitHub Actions.

For editor/tooling against an editable install (optional): pip install -e ".[dev]" in whatever environment your IDE uses.

License

Apache-2.0 (see LICENSE at the repository root and in the published PyPI package when released).

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

intentproof_sdk-0.1.2.tar.gz (27.2 kB view details)

Uploaded Source

Built Distribution

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

intentproof_sdk-0.1.2-py3-none-any.whl (34.3 kB view details)

Uploaded Python 3

File details

Details for the file intentproof_sdk-0.1.2.tar.gz.

File metadata

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

File hashes

Hashes for intentproof_sdk-0.1.2.tar.gz
Algorithm Hash digest
SHA256 e691f172a3af030f8777d8f7901e22b14fef4aed30938f7c8950e9bde675d9d3
MD5 fa6957acb7a7cd57a10c0347717f498c
BLAKE2b-256 0d5cf41af89ff97c7503059126250a829d43f58a33c953474f0fee441546a00b

See more details on using hashes here.

Provenance

The following attestation bundles were made for intentproof_sdk-0.1.2.tar.gz:

Publisher: release.yml on IntentProof/intentproof-sdk-python

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

File details

Details for the file intentproof_sdk-0.1.2-py3-none-any.whl.

File metadata

File hashes

Hashes for intentproof_sdk-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 27062b87d479a281ec150f17389994f35294204419fa002869fa045a505b44a4
MD5 29376fa9dd1abdab473c182164b0b241
BLAKE2b-256 f281d3b925ff6b182b6b618c979a324aa20c2acdc4f1ade30c6b7bbc816694ed

See more details on using hashes here.

Provenance

The following attestation bundles were made for intentproof_sdk-0.1.2-py3-none-any.whl:

Publisher: release.yml on IntentProof/intentproof-sdk-python

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