affinecap
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, ortransitionclaim 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_forkcallback.
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.lineageis an immutable tuple of registry-backedLineageEntryvalues.cap.capability_idis the current entry's random public identifier.cap.generationis 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 withissue, not by callingCapability.LineageEntryis the immutable public lineage record type.CapabilityErroris the base class for lifecycle failures.CapabilityConsumedErrormeans the handle is no longer live.CapabilityProcessErrormeans an operation crossed a process or runtime boundary.CapabilityIntegrityErrormeans 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)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| affinecap-0.1.1.tar.gz | 29.0 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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 logRelease 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 logSecurity 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.
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 logA 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.