symfonic.capabilities.human.consumption¶
consumption ¶
Single-winner consumption (SEC-PTK-3, LIB-TL-1..3).
Two backings, one contract. Neither of them reads before it writes: the whole
property being bought is that concurrent redeemers of one jti are decided by
a single atomic operation, and a get followed by a put is two operations
with a race between them — which is the shape LIB-TL-2 spells out as FORBIDDEN.
Library mode reaches the adopter's persistence backend through
:class:ConditionalWriteConsumption; the operated platform reaches its
authoritative ledger, which satisfies the same port. Nothing above this module
branches on which one it got.
ConditionalWriteConsumption ¶
ConditionalWriteConsumption(backend: Any, *, durable: bool | None = None, clock: Callable[[], float] = time.time)
LIB-TL-2 — one atomic conditional write on the adopter's backend.
The port has exactly one verb because a port that also offered a read would
invite the read-then-write sequence the contract forbids. claim is a
single await: there is no branch in this method that could become two
round-trips later.
Source code in src/symfonic/capabilities/human/consumption.py
ConsumptionRecord
dataclass
¶
What a redemption writes. Ids only — no payload, no answer.
InMemoryConsumption ¶
The in-process reference implementation (LIB-TL-2's single-lock case).
Correct within one process and honest about the rest: durable is
False, and :func:require_durable_consumption is what stops a
deployment with durable checkpoints from quietly using it.
Source code in src/symfonic/capabilities/human/consumption.py
declared_durability ¶
What a backend says about surviving a restart, read one way everywhere.
Two ports in this package spell the same fact differently — durable is an
attribute on an adopter's persistence backend and a method on the checkpoint
command port — and an adopter who wires one object into both should not get
"volatile" from one of them because of the parentheses. Silence still means
volatile: assuming durability nobody asserted is how SEC-PTK-7's honesty rule
gets lost quietly, and a wrong guess in the other direction only produces a
refusal an operator can read.
Source code in src/symfonic/capabilities/human/consumption.py
require_durable_consumption ¶
SEC-PTK-7 — durable checkpoints must not get a volatile jti store.
A deployment whose checkpoints survive a restart and whose consumed set does not has single-use enforcement only until the next deploy: every token minted before the restart becomes redeemable a second time, and nothing in the logs says so.
A fully volatile deployment is supported, not an error — that is the developer laptop, and its tokens die with the process anyway.