Skip to main content

affinecap

CI

One-shot runtime authority for Python.

Python assignment freely creates aliases. That is usually helpful, but it is awkward when only one contender should be allowed to claim a deployment approval, publication grant, or destructive-operation authority. affinecap keeps a value in an interpreter-local registry and gives the application an opaque handle. The first library-mediated claim of that handle wins; every alias of the spent handle becomes stale.

from affinecap import CapabilityConsumedError, issue

publish = issue(value=lambda: "published", label="review passed")

assert publish.consume(lambda action: action()) == "published"

try:
    publish.consume(lambda action: action())
except CapabilityConsumedError:
    print("already used")

This is a small coordination primitive for cooperative Python code. It provides dynamic affine-style behavior; it does not add linear types or a security sandbox to Python.

What it enforces

  • At most one consume, transfer, or transition claim succeeds for each issued handle. A successor is a new handle with its own one-shot claim.
  • All Python aliases of a spent handle are stale.
  • The registry accepts the exact handle object it issued, not a reconstructed lookalike.
  • Claims are linearized across threads in one interpreter before user code runs.
  • Default copy.copy, copy.deepcopy, standard pickle protocols, inherited fork use, and multiprocessing pickling are rejected.
  • Handles are bound to the issuing process ID and interpreter runtime epoch.
  • Transfer and transition successors carry registry-maintained ancestry.
  • Once callback execution begins, an exception leaves the predecessor spent.

What it does not enforce

  • It is not a boundary against hostile code in the same interpreter.
  • It cannot erase references to the underlying value that exist outside the registry.
  • It cannot stop the winning callback from retaining or repeatedly using the released value; callback code is trusted.
  • It does not authenticate who may call issue.
  • It does not make external effects transactional, durable, or exactly-once.
  • Callback code that forks can continue executing in both processes. Do not fork inside a callback when duplicate effects would matter.
  • Lineage is neither signed nor durable provenance.
  • Capabilities cannot move between interpreters or processes, and they do not survive interpreter restart.
  • The API is not safe to invoke from a signal handler or a user-defined os.register_at_fork callback.

The exact guarantee tiers and assumptions are in the semantic contract.

Installation

The package supports CPython 3.10 through 3.14 and has no runtime dependencies.

python -m pip install affinecap

From a source checkout:

python -m pip install .

API

issue(value, *, label=None) -> Capability

Creates one live capability. The public handle stores no value or lineage in its instance state; the interpreter-local registry retains both and binds the record to the exact handle, current process ID, and runtime epoch.

Labels are application-supplied diagnostic metadata exposed through cap.lineage. Do not put secrets, credentials, or sensitive personal data in them.

cap.consume(consumer)

Claims the capability before calling consumer(value) and returns the callback's result. A callback exception propagates and the capability remains spent. This makes callback admission one-shot; it does not constrain how the callback uses or retains value after release.

approval = issue(value={"artifact": "sha256:..."})
receipt = approval.consume(deploy)

Callbacks are invoked as ordinary synchronous Python calls. If an async def function is supplied, calling it merely produces a coroutine; if a generator function is supplied, calling it merely produces a generator. consume returns that lazy object without executing it, and any later exception is outside the capability callback boundary.

cap.transfer(*, label=None) -> Capability

Returns a new handle for the same value and invalidates the old handle. This is a logical custody handoff, not a Python or Rust memory move. Existing references to the underlying value are unaffected.

worker_cap = approval.transfer(label="approval service -> worker")

cap.transition(transform, *, label=None) -> Capability

Claims the predecessor, synchronously calls transform(value), and places the returned value behind a new successor. If the synchronous call raises, there is no successor and the predecessor stays spent.

verified = acquired.transition(verify, label="verification passed")

As with consume, a coroutine, generator, iterator, or other lazy object returned by transform is treated as the returned value itself. It becomes the successor payload; later execution and failures are not observed by transition. Run all work that must be covered by the fail-closed boundary before the transform returns.

Live-handle properties

  • cap.lineage is an immutable tuple of registry-backed LineageEntry values.
  • cap.capability_id is the current entry's random public identifier.
  • cap.generation is the number of transfers or transitions since issuance.

These properties require a live handle. They raise a lifecycle error after the handle is spent. A LineageEntry contains capability_id, parent_id, generation, operation, and optional label. The identifiers and entries are diagnostic metadata; they do not grant authority and are not authenticity proofs.

Exported types and errors

  • Capability[T] is the final, opaque generic handle type. Applications create handles with issue, not by calling Capability.
  • LineageEntry is the immutable public lineage record type.
  • CapabilityError is the base class for lifecycle failures.
  • CapabilityConsumedError means the handle is no longer live.
  • CapabilityProcessError means an operation crossed a process or runtime boundary.
  • CapabilityIntegrityError means a handle is malformed or is not the exact live object issued by this runtime.
  • __version__ reports the installed distribution version.

Construction, subclassing, and default standard-library copying and serialization raise TypeError rather than a CapabilityError subclass.

Realistic example

examples/deployment_approval.py checks an artifact against an independently supplied approved digest, hands the authority to a worker, and records an action in a stateful simulated deployment backend. It demonstrates at-most-one admission through the handle, not exactly-once deployment semantics:

python examples/deployment_approval.py

The smaller executable version of the opening example is examples/minimal.py.

Design notes

The registry stores the value, immutable lineage, issuing process ID/runtime epoch, and a weak reference to the exact handle. The epoch is an in-memory marker for the current registry instance and is replaced after a fork. A synchronization lock atomically removes a claimed record before any callback is invoked. Transfer and transition issue a new record; they never reactivate the predecessor.

A fork hook replaces the child's lock, epoch, and active registry without touching potentially locked parent state. Inherited records are quarantined until child shutdown so dropping them cannot run arbitrary payload finalizers inside the fork hook. A long-running child may therefore retain inherited payload resources until it exits.

Dropping an unused handle is allowed—that is why the model is affine rather than linear. A weak-reference callback releases the registry's value reference on a best-effort basis. Do not rely on abandonment for resource cleanup. In particular, if the registered value retains its own capability handle, the registry-to-value-to-handle reference path keeps the handle alive and prevents weak-reference abandonment; explicit consumption is required to break that retention.

See prior art for comparisons with static affine/linear systems, object-capability patterns, contextvars.Token, state-machine libraries, and once primitives.

Design provenance

The design was generalized from selected mechanisms in Reverse Gravity, a scientific data-processing system that needed one-shot custody handoffs and fail-closed phase transitions. affinecap keeps the reusable in-memory mechanism and omits that system's data formats, network access, artifact layout, and workflow-specific transition rules. The package implementation is a compact generalization, not a verbatim source copy. See the design provenance note.

Development

python -m pip install -e '.[dev]'
pytest
ruff check .
ruff format --check .
mypy
python -m build
twine check dist/*

CI runs the tests on Python 3.10 through 3.14 and checks formatting, lint, typing, distributions, and both examples.

License

MIT

Metadata

Release files for affinecap 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for affinecap 0.1.1
File Size Uploaded Provenance
affinecap-0.1.1.tar.gz 29.0 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for affinecap 0.1.1
File Interpreter ABI Platform
affinecap-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 39.4 kB

Release files / affinecap-0.1.1.tar.gz

Download URL affinecap-0.1.1.tar.gz
Size 29.0 kB
Tags Source
SHA-256 checksum
How to use checksums
0ba3625f9e2b430115979db7e45dbf13a1cbac7fcc28cea72dc3a34d822a5456
BLAKE2b-256 checksum
How to use checksums
bd81215c593f9a8f1131a2f3075d024b260ede6591c9b2cbdb57ca5a825d8401
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 3, 2026.

Transparency log

Release files / affinecap-0.1.1-py3-none-any.whl

Download URL affinecap-0.1.1-py3-none-any.whl
Size 10.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
eb16772648a117ad61507645b5e47eb23e4026da69184afd8195a23750fad6a6
BLAKE2b-256 checksum
How to use checksums
316ce10d00341fcab5082cf049af65789f4af64b669815504e811665ad9f59de
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 3, 2026.

Transparency log

Security for affinecap 0.1.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for affinecap-0.1.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 3, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page