Files
artifact-store/src/artifactstore/dataplane/spi.py

105 lines
2.9 KiB
Python

"""Data plane SPI (ADR-0004).
The control plane (``registry`` / ``api`` / ``retention`` / ``audit``)
interacts with bytes exclusively through this surface. v1 ships an
in-process implementation backed by a :class:`StorageBackend`; a future
Rust daemon will satisfy the same protocol over a Unix socket.
The SPI uses CBOR-serialisable input and output types so the in-process
implementation and a future RPC implementation share representations.
"""
from __future__ import annotations
from collections.abc import AsyncIterator
from dataclasses import dataclass
from typing import Protocol, runtime_checkable
from artifactstore.identity import ContentAddress, Digest
from artifactstore.storage.spi import BackendStatus, DeletionResult, StorageReceipt
__all__ = [
"DataPlane",
"IngestHints",
"IngestResult",
"VerifyResult",
]
@dataclass(frozen=True, slots=True)
class IngestHints:
"""Optional hints attached to an ingest call.
``size_hint`` lets backends pre-allocate or pick an upload strategy.
``primary_algorithm`` overrides the default primary hash (BLAKE3).
``backend_id`` lets the data plane select among configured backends
(ignored when only one backend is configured).
"""
size_hint: int | None = None
primary_algorithm: str | None = None
backend_id: str | None = None
@dataclass(frozen=True, slots=True)
class IngestResult:
"""Return value of :meth:`DataPlane.ingest_stream`."""
primary_digest: Digest
sha256_digest: Digest
size_bytes: int
receipt: StorageReceipt
@dataclass(frozen=True, slots=True)
class VerifyResult:
"""Return value of :meth:`DataPlane.verify_object`.
``verified`` is ``True`` when the bytes currently held at
``content_address`` re-digest to the expected primary digest. ``mismatch``
is populated with a short explanation when verification fails.
"""
content_address: ContentAddress
verified: bool
actual_primary_digest: Digest
actual_sha256_digest: Digest
actual_size_bytes: int
mismatch: str | None = None
@runtime_checkable
class DataPlane(Protocol):
"""Byte-handling surface used by the control plane."""
async def ingest_stream(
self,
stream: AsyncIterator[bytes],
*,
hints: IngestHints | None = None,
) -> IngestResult: ...
async def serve_object(
self,
content_address: ContentAddress,
*,
byte_range: tuple[int, int] | None = None,
backend_id: str | None = None,
) -> AsyncIterator[bytes]: ...
async def verify_object(
self,
content_address: ContentAddress,
*,
backend_id: str | None = None,
) -> VerifyResult: ...
async def delete_object(
self,
content_address: ContentAddress,
*,
backend_id: str | None = None,
) -> DeletionResult: ...
async def backend_health(self) -> BackendStatus: ...