Skip to content
Menu

Documentation

sandloom.sdk.infra

Infrastructure accessors, settings, the core runtime and (advanced) the facade.

Public API 1.0 (spec §2.2, Appendix C).

from sandloom.sdk.infra import <name>. Signatures in a terminal: sandloom docs <name>. Beginner names are the ones generated code and the examples use; any other name is advanced (mark its import line # sandloom: advanced <reason> in generated code and examples).

Advanced

Name Kind Summary
Cache class What a module calls on cache(): the operations, not the lifecycle.
cache function Return the operation-based CacheProvider for a named instance.
capability function Return the instance name of the capability ref points to, typed by the ref. A capability's package publishes the ref and the caller's...
CapabilityRef class A typed key to a capability: CapabilityRef[Search]("search.v1").
claims_contract_name function "tenancy" -> "tenancy.claims.v1".
CLAIMS_CONTRACT_SUFFIX constant
ClaimsContributorContract class <module>.claims.v1 — a module's token-claims contributors as a contract.
core_from_request function SYNC accessor of the request's CoreRuntime (direct calls).
CoreFacade class A module's strictly bounded access point to the framework's infrastructure.
CoreRuntime class Read-only runtime view of the CoreFacade for module code.
facts_contract_name function "billing" -> "billing.facts.v1".
FACTS_CONTRACT_SUFFIX constant
get_core async function Return the read-only CoreRuntime view of the app's facade.
Notification class One outbound notification, independent of the delivery backend.
Notifier class What a module calls on notifier().
notifier function Return the NotifierProvider for a named instance.
ObjectStore class What a module calls on objectstore().
objectstore function Return the ObjectStoreProvider for a named instance (the module-level twin of CoreRuntime.objectstore; spec §2.2).
settings settings object
ShutdownTask constant
StartupTask constant
TaskQueue class What a module calls on taskqueue().
taskqueue function Return the TaskQueueProvider for a named instance (the module-level twin of CoreRuntime.taskqueue; spec §2.2).

Advanced names

Cache

class Cache(Protocol)

What a module calls on cache(): the operations, not the lifecycle.

Cache.get

async def get(self, key: str) -> str | None

Cache.set

async def set(self, key: str, value: str, *, ttl_seconds: int | None = None) -> None

Cache.add

async def add(self, key: str, value: str, *, ttl_seconds: int | None = None) -> bool

Cache.delete

async def delete(self, key: str) -> None

Cache.incr

async def incr(self, key: str, *, ttl_seconds: int | None = None) -> int

Cache.get_many

async def get_many(self, keys: Sequence[str]) -> list[str | None]

Cache.set_many

async def set_many(
    self,
    items: Mapping[str, str],
    *,
    ttl_seconds: int | None = None,
) -> None

Cache.compare_and_set

async def compare_and_set(
    self,
    key: str,
    expected: str,
    value: str,
    *,
    ttl_seconds: int | None = None,
) -> bool

Cache.compare_and_delete

async def compare_and_delete(self, key: str, expected: str) -> bool

Cache.incr_window

async def incr_window(
    self,
    key: str,
    previous_key: str,
    *,
    ttl_seconds: int | None = None,
) -> tuple[int, str | None]

Cache.incr_many

async def incr_many(
    self,
    keys: Sequence[str],
    *,
    ttl_seconds: int | None = None,
) -> list[int]

Cache.set_nx_get

async def set_nx_get(
    self,
    key: str,
    value: str,
    *,
    ttl_seconds: int | None = None,
) -> tuple[bool, str | None]

Cache.set_max

async def set_max(self, key: str, value: str, *, ttl_seconds: int | None = None) -> str

cache

def cache(name: str = 'default') -> Cache

Return the operation-based CacheProvider for a named instance.

The sanctioned cache surface for modules and core guards — get/set/ add/delete/incr (see sandloom.core.infra.contracts.CacheProvider). Resolves via the process-active InfraRegistry (RuntimeError when none is bound); the registry's configuration picks the driver, so this surface never names a concrete backend.

capability

def capability(ref: CapabilityRef[P], name: str = 'default') -> P

Return the instance name of the capability ref points to, typed by the ref. A capability's package publishes the ref and the caller's Protocol, and the capability's major version must match the ref's.

CapabilityRef

class CapabilityRef(Generic)(value: str) -> None

A typed key to a capability: CapabilityRef[Search]("search.v1").

The caller's Protocol travels in the type, and a .vN suffix is checked against the capability when it is resolved; a ref without one skips that check. A class passed as type[T] cannot do this: mypy refuses an abstract class or a Protocol there.

CapabilityRef.name

name  (property)

CapabilityRef.version

version  (property)

The vN suffix, if any. Only a versioned ref is checked on resolve.

claims_contract_name

def claims_contract_name(owner: str) -> str

"tenancy" -> "tenancy.claims.v1".

CLAIMS_CONTRACT_SUFFIX

CLAIMS_CONTRACT_SUFFIX: str = '.claims.v1'

(no docstring)

ClaimsContributorContract

class ClaimsContributorContract(facade: CoreFacade, owner: str | None) -> None

<module>.claims.v1 — a module's token-claims contributors as a contract.

Auto-registered for every module that adds a claims contributor; runs the module's contributors (in its own read-only Unit of Work, like any read contract) and returns their merged claims. A token issuer in another process reaches it over internal RPC via SERVICE_BINDINGS.

ClaimsContributorContract.contribute

async def contribute(self, user_id: Any) -> dict[str, Any]

Merged claims of this module's contributors for user_id.

core_from_request

def core_from_request(request: Request) -> CoreRuntime

SYNC accessor of the request's CoreRuntime (direct calls).

For code that holds a Request and cannot await: the same value as await get_core(request). As a dependency use Depends(get_core) (async: no thread-pool hop), never Depends(core_from_request).

CoreFacade

class CoreFacade(_ClaimsMixin, _RegistriesMixin)(
    app: FastAPI,
    settings_obj: Settings | None = None,
    service_registry: ServiceRegistry | None = None,
    get_db: Callable[[], Awaitable[AsyncSession]] | Callable[[], AsyncGenerator[AsyncSession, None]] | None = None,
) -> None

A module's strictly bounded access point to the framework's infrastructure.

Provides only methods that are safe and stable for modules to depend on; concrete infrastructure details (cache backend, tracing implementation, transport) stay hidden behind this API. One instance is created by the composition root and handed to every module's register(core).

CoreFacade.event_bus

event_bus  (property)

Return the event bus owned by this facade.

The instance is bound via set_event_bus() during app wiring so each application owns (and shuts down) exactly its own bus. Falls back to the bare-facade in-memory bus for facades that never bind one (unit tests).

CoreFacade.set_event_bus

def set_event_bus(self, bus: Any) -> None

Bind the event bus instance this application owns.

CoreFacade.add_startup_task

def add_startup_task(
    self,
    task: StartupTask,
    *,
    critical: bool = False,
    phase: str | None = None,
) -> None

Register a startup task.

critical=True means the application MUST NOT start if the task fails: run_startup re-raises instead of degrading silently.

phase is "infra" (runs first), "modules" (the default) or "post" (runs last); a task's name never selects its phase.

CoreFacade.is_critical_startup_task

def is_critical_startup_task(self, task: Any) -> bool

CoreFacade.startup_task_specs

startup_task_specs  (property)

(task, critical) pairs in registration order.

CoreFacade.startup_task_phased_specs

startup_task_phased_specs  (property)

(task, critical, phase) triples in registration order.

CoreFacade.add_shutdown_task

def add_shutdown_task(self, task: ShutdownTask) -> None

CoreFacade.register_health_check

def register_health_check(self, name: str, fn: HealthCheck) -> None

CoreFacade.health_checks

health_checks  (property)

CoreFacade.startup_tasks

startup_tasks  (property)

CoreFacade.shutdown_tasks

shutdown_tasks  (property)

CoreFacade.module_registration

def module_registration(self, module_name: str) -> Iterator[ModuleAPI]

Scope a module registration so a failure leaves no partial state.

Yields a draft-backed ModuleAPI: everything the module registers (routers, services, lifecycle hooks, health checks, event schemas) is buffered in a ModuleRegistrationDraft and applied atomically by commit only when register() returns normally. A failure propagates without ever committing, so NOTHING is applied — including event schemas, which the old snapshot/restore path left behind as orphans in the contract inventory.

This replaces snapshotting private kernel state (service_registry._map, app.router.routes slicing): the kernel is never mutated until commit.

CoreFacade.ensure_tracer

def ensure_tracer(self) -> None

Initialize tracing once (idempotent).

CoreFacade.span

def span(self, name: str) -> Any

Return a span context manager (no-op if tracing disabled).

Usage: with core.span("my.operation"): ...

CoreFacade.register_metric

def register_metric(self, name: str, value: Any | None = None) -> None

CoreFacade.increment_metric

def increment_metric(self, name: str, amount: int | float = 1) -> None

Atomically add amount to the counter name.

A real counter (monotonic, thread-safe): the in-process value (get_metric) is updated under a lock and the increment is exported to the Prometheus counter sandloom_facade_counter_total{name=...}. Use it instead of a get_metric/register_metric read-modify-write.

CoreFacade.get_metric

def get_metric(self, name: str) -> Any

CoreFacade.list_metrics

def list_metrics(self) -> dict[str, Any]

CoreFacade.logger_factory

def logger_factory(self, name: str) -> logging.Logger

Return namespaced module logger (sandloom.module.<name>).

CoreRuntime

class CoreRuntime(facade: CoreFacade) -> None

Read-only runtime view of the CoreFacade for module code.

Request-time module code (Depends(get_core)) gets THIS, not the full facade: it can resolve contracts and capabilities, read settings, log, trace, count, and collect token claims — but it cannot reach the event bus (publish through publish_event), the infrastructure registry, the raw FastAPI app, the service registry, or any register_* mutator. Registration happens only through the sandloom.core.module_api.ModuleAPI handed to register(core).

This is an API-discipline boundary for trusted application code, not an immutable object graph or a sandbox. Shared Settings must be treated as read-only by modules; the remote-fact cache is maintained by shared authz machinery. Do not mutate either from a business module.

CoreRuntime.get_service

def get_service(self, name: str) -> Any

CoreRuntime.collect_claims

async def collect_claims(
    self,
    user_id: Any,
    issuer_session: Any = None,
    *,
    issuer: str | None = None,
    precomputed: Mapping[Any, dict[str, Any] | None] | None = None,
) -> dict[str, Any]

CoreRuntime.get_access_fact

def get_access_fact(self, name: str) -> Any

CoreRuntime.remote_fact_owners

def remote_fact_owners(self) -> list[str]

CoreRuntime.remote_access_fact_cache

remote_access_fact_cache  (property)

CoreRuntime.capability

def capability(
    self,
    capability: str,
    name: str = 'default',
    contract: type | None = None,
) -> Any

CoreRuntime.require_capability

def require_capability(
    self,
    capability: str,
    name: str = 'default',
    *,
    features: set[str] | None = None,
    contract: type | None = None,
    version: str | None = None,
) -> Any

CoreRuntime.database

def database(self, name: str = 'default') -> Any

CoreRuntime.cache

def cache(self, name: str = 'default') -> Any

CoreRuntime.objectstore

def objectstore(self, name: str = 'default') -> Any

CoreRuntime.taskqueue

def taskqueue(self, name: str = 'default') -> Any

CoreRuntime.notifier

def notifier(self, name: str = 'default') -> Any

CoreRuntime.settings

settings  (property)

CoreRuntime.logger

logger  (property)

CoreRuntime.logger_factory

def logger_factory(self, name: str) -> logging.Logger

CoreRuntime.span

def span(self, name: str) -> Any

CoreRuntime.increment_metric

def increment_metric(self, name: str, amount: int | float = 1) -> None

CoreRuntime.get_metric

def get_metric(self, name: str) -> Any

facts_contract_name

def facts_contract_name(owner: str) -> str

"billing" -> "billing.facts.v1".

FACTS_CONTRACT_SUFFIX

FACTS_CONTRACT_SUFFIX: str = '.facts.v1'

(no docstring)

get_core

async def get_core(request: Request) -> CoreRuntime

Return the read-only CoreRuntime view of the app's facade.

Module code gets contract/capability resolution, settings, logging, tracing and counters — never the event bus, the infra registry, the raw app or the register_* mutators (those belong to register(core)).

async on purpose: it does no I/O, and FastAPI runs a SYNC dependency in the anyio thread pool on every request (a thread hop for an attribute read). Use it as Depends(get_core); a direct call is core = await get_core(request).

Notification

class Notification(
    channel: NotificationChannel,
    recipient: str,
    subject: str,
    body: str,
    data: Mapping[str, Any] = <factory>,
    dedupe_key: str | None = None,
    body_fields: Mapping[str, str] = <factory>,
) -> None

One outbound notification, independent of the delivery backend.

A logical message: which channel to deliver on, the recipient address for that channel (an email, phone, device token, or user id for inapp), a short subject and body, plus structured data and an optional dedupe_key a driver may use to suppress duplicate sends.

body_fields are the values the body was built from, so a driver can render its own template. They are as sensitive as the body (a secret link, a name): a driver never logs them, and data stays safe to record.

Notifier

class Notifier(Protocol)

What a module calls on notifier().

Notifier.send

async def send(self, notification: Notification) -> None

notifier

def notifier(name: str = 'default') -> Notifier

Return the NotifierProvider for a named instance.

The sanctioned outbound-notification surface for modules: build a sandloom.core.infra.contracts.Notification and await notifier().send(...) — never import an email/SMS SDK. Resolves via the process-active InfraRegistry (RuntimeError when none is bound).

ObjectStore

class ObjectStore(Protocol)

What a module calls on objectstore().

ObjectStore.put

async def put(
    self,
    key: str,
    data: bytes | Any,
    *,
    content_type: str | None = None,
    metadata: Mapping[str, str] | None = None,
) -> str

ObjectStore.get

async def get(self, key: str) -> bytes

ObjectStore.delete

async def delete(self, key: str) -> None

ObjectStore.url

async def url(self, key: str) -> str

objectstore

def objectstore(name: str = 'default') -> ObjectStore

Return the ObjectStoreProvider for a named instance (the module-level twin of CoreRuntime.objectstore; spec §2.2).

settings

settings: Settings  # loaded and validated at first attribute access

(no docstring)

ShutdownTask

ShutdownTask: UnionType

(no docstring)

StartupTask

StartupTask: UnionType

(no docstring)

TaskQueue

class TaskQueue(Protocol)

What a module calls on taskqueue().

TaskQueue.enqueue

async def enqueue(
    self,
    task: str,
    payload: Mapping[str, Any],
    *,
    delay_seconds: int | None = None,
    idempotency_key: str | None = None,
) -> str

taskqueue

def taskqueue(name: str = 'default') -> TaskQueue

Return the TaskQueueProvider for a named instance (the module-level twin of CoreRuntime.taskqueue; spec §2.2).