v1
background_run_grants record, v1 — canonical aliases.
Consumers and the record registry import from here and never touch the inner
module names, mirroring flow_specs/v1/__init__.py.
Module
Submodules
- bitfount.cache.types.background_run_grants.v1.migrations - No inbound migration — this is the base version.
- bitfount.cache.types.background_run_grants.v1.model - Pydantic record for the
background_run_grantscache table (v1). - bitfount.cache.types.background_run_grants.v1.schema - SQLAlchemy ORM for the
background_run_grantscache table (v1). - bitfount.cache.types.background_run_grants.v1.store - CRUD for the
background_run_grantsrecord (v1).
Classes
Record
class Record(**data: Any):The token grant a background DAG child redeems for Hub and EHR tokens.
A background DAG run executes in a Prefect-launched subprocess, which inherits nothing from the pod: no Hub session, and no way to reach whatever the pod's credential is bound to, since neither a live handle nor a bearer token can travel as a JSON deployment parameter — Prefect persists those in its database in clear. The run presents the nonce recorded here to the pod's loopback child-config endpoint instead, and the cache is how it learns that nonce: it is the only store both processes already share.
What this is for depends on how the pod authenticates. On the desktop
path the pod's credential is a RefreshableJWT bound to a queue back to
the app, so the endpoint is the only way the run can get a token at all.
On the Docker path the pod authenticates with API keys, which the run reads
from the environment it was launched with, exactly as the pod did — there
the grant buys no token, and the pod says so rather than refusing one.
The grant is still needed on both paths, because tokens are not all it
redeems: the run also fetches the pod's ehr_config (which carries a
private key, so it cannot be a parameter either) and the import paths of
the hooks it must re-register. A pod with no control server at all — a
notebook — issues no grant, and its runs fall back to the environment for
everything.
One grant per lineage, not per run. The pod rotates it on a submit that
finds no live run of the lineage and renews it, nonce unchanged, on one that
does, so a lineage never has two grants in flight. Deliberately not
consumed on first use: a background run outlives its first token, and
RefreshableJWT.get_token redeems the grant again on every refresh for the
length of the run.
Attributes
task_hash: The(pod, datasource)task hash the grant is scoped to.project_id: The project the run belongs to. Part of the key because two projects on one datasource share a task_hash and must not share a grant.nonce: The secret the child presents to the token endpoint.issued_at: When the pod wrote this grant.expires_at: When the endpoint must stop honouring it. A bound on how long a leaked grant is worth anything, not a statement about therun: a run still going when its grant expires fails its next token refresh, so the TTL has to exceed the longest run comfortably.tags: Arbitrary flat metadata.
Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
Variables
- static
expires_at : datetime.datetime
- static
issued_at : datetime.datetime
- static
model_config
- static
nonce : str
- static
project_id : str
- static
tags : dict[str, typing.Any] | None
- static
task_hash : str
ORM
class ORM(**kwargs):SQLAlchemy model for the background_run_grants cache table.
One row per lineage, rewritten by the pod on every background DAG submit
and read by the child that run launches. Holds a credential, which is why
it is a table of its own rather than more keys in flow_specs.tags: that
column is general-purpose bookkeeping and is logged as a unit in places a
secret must not appear.
A simple constructor that allows initialization from kwargs.
Sets attributes on the constructed instance using the names and
values in kwargs.
Only keys that are present as attributes of the instance's class are allowed. These could be, for example, any mapped columns or relationships.
Ancestors
Variables
-
expires_at : Union[sqlalchemy.orm.attributes.InstrumentedAttribute[+_T_co], +_T_co]
-
issued_at : Union[sqlalchemy.orm.attributes.InstrumentedAttribute[+_T_co], +_T_co]
-
nonce : Union[sqlalchemy.orm.attributes.InstrumentedAttribute[+_T_co], +_T_co]
-
project_id : Union[sqlalchemy.orm.attributes.InstrumentedAttribute[+_T_co], +_T_co]
-
tags : Union[sqlalchemy.orm.attributes.InstrumentedAttribute[+_T_co], +_T_co]
-
task_hash : Union[sqlalchemy.orm.attributes.InstrumentedAttribute[+_T_co], +_T_co]